diff --git a/.github/workflows/wai-quantum-formats.yml b/.github/workflows/wai-quantum-formats.yml new file mode 100644 index 000000000..89cc3e93d --- /dev/null +++ b/.github/workflows/wai-quantum-formats.yml @@ -0,0 +1,174 @@ +name: wai-quantum other formats + +# The readers and writers of other formats (wai/wai-quantum/src/engine/formats/): +# QIR under its base and adaptive profiles and jeff through the crate's own +# Cap'n Proto reader and writer. Their unit tests (the wire format, the +# OpenQASM they write, every intrinsic and gate held to its matrix, each +# profile's refusals with their lines, the Pauli-product rotation against +# exp(iθP), every prefix of a module refused), and the conformance set +# (wai/quantum-conformance/formats/: the QIR specification's examples, the +# jeff project's benchmarks and examples, hand-built modules and OpenQASM +# sources written to each format, each with what the engine must do with it, +# the QIR written pinned by its BLAKE3), and hostile inputs: every file +# of the set mutated, each converted or refused with a code within its +# bounds, natively and under wasm32-wasip2. +# +# The set runs natively, where a second test holds its bundle equal to its +# files, and under wasm32-wasip2 by wasmtime, where the cases come from the +# bundle the test compiles in and the QIR written must be the same bytes. + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: [main] + paths: + - 'wai/wai-quantum/**' + - 'wai/quantum-conformance/formats/**' + - 'rust-toolchain.toml' + - '.github/workflows/wai-quantum-formats.yml' + pull_request: + paths: + - 'wai/wai-quantum/**' + - 'wai/quantum-conformance/formats/**' + - 'rust-toolchain.toml' + - '.github/workflows/wai-quantum-formats.yml' + +jobs: + relevant: + name: does this change touch it + runs-on: linux-arm64 + outputs: + run: ${{ steps.gate.outputs.run }} + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + - id: gate + run: | + set -euo pipefail + r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-quantum-formats.yml \ + "${{ github.event.pull_request.base.sha }}" \ + "${{ github.event.pull_request.head.sha }}") + echo "this change touches the readers of other formats: $r" + echo "run=$r" >> "$GITHUB_OUTPUT" + + native: + name: readers, writers and the set · linux + needs: relevant + if: needs.relevant.outputs.run == 'true' + runs-on: linux + timeout-minutes: 60 + steps: + - uses: actions/checkout@v5 + - name: Install Rust 1.98.0 + shell: bash + run: | + export PATH="$HOME/.cargo/bin:$PATH" + command -v rustup >/dev/null 2>&1 || \ + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0 + rustup toolchain install 1.98.0 --profile minimal + rustup default 1.98.0 + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + # A module filter is satisfied by any one test of the module, so each + # module's tests are required by name to have run. + - name: The readers' and writers' unit tests + shell: bash + working-directory: wai/wai-quantum + run: | + set -euo pipefail + cargo test --locked --release --features full --lib engine::formats 2>&1 | tee "$RUNNER_TEMP/formats.log" + grep -qE '^test result: ok\. [1-9][0-9]+ passed; 0 failed' "$RUNNER_TEMP/formats.log" || { echo "::error::the formats' tests did not pass"; exit 1; } + for m in engine::formats::capnp::tests engine::formats::emit::tests engine::formats::tests::every_gate_is_written_exactly engine::formats::tests::a_pauli_product_rotation_is_exp_i_theta_p engine::formats::tests::the_conformance_set_holds_these_modules engine::formats::tests::a_register_too_large_is_refused_before_it_is_allocated; do + grep -q "^test $m.* \.\.\. ok$" "$RUNNER_TEMP/formats.log" || { echo "::error::no test of $m ran"; exit 1; } + done + - name: The conformance set + shell: bash + working-directory: wai/wai-quantum + run: | + set -euo pipefail + cargo test --locked --release --features full --test formats_conformance 2>&1 | tee "$RUNNER_TEMP/set.log" + grep -q 'test result: ok. 2 passed; 0 failed' "$RUNNER_TEMP/set.log" || { echo "::error::the conformance set did not pass"; exit 1; } + # Hostile inputs: every file of the set mutated by a seeded generator + # (bits flipped, files cut short and spliced, and every length, count, + # offset and 32-bit field inflated), each converted or refused with a + # code within its time and memory bounds. The count of inputs is pinned. + - name: Hostile inputs + shell: bash + working-directory: wai/wai-quantum + run: | + set -euo pipefail + cargo test --locked --release --features full --test formats_mutation -- --nocapture 2>&1 | tee "$RUNNER_TEMP/mutation.log" + grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/mutation.log" || { echo "::error::a hostile input was not refused cleanly"; exit 1; } + grep -q '45153 inputs: ' "$RUNNER_TEMP/mutation.log" || { echo "::error::the count of hostile inputs is not the pinned 45153"; exit 1; } + - name: The readers without the receipts + shell: bash + working-directory: wai/wai-quantum + run: | + set -euo pipefail + cargo check --locked --no-default-features --features quantum_engine --lib + + wasip2: + name: the set under wasm32-wasip2 (wasmtime) + needs: relevant + if: needs.relevant.outputs.run == 'true' + runs-on: linux + timeout-minutes: 60 + steps: + - uses: actions/checkout@v5 + - name: Install Rust 1.98.0 with the wasm32-wasip2 target + shell: bash + run: | + export PATH="$HOME/.cargo/bin:$PATH" + command -v rustup >/dev/null 2>&1 || \ + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.98.0 + rustup toolchain install 1.98.0 --profile minimal + rustup default 1.98.0 + rustup target add --toolchain 1.98.0 wasm32-wasip2 + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + # The release binary, pinned by version and SHA-256, rather than a build from source. + - name: Install wasmtime 49.0.2 + shell: bash + run: | + set -euo pipefail + [ "$(uname -m)" = x86_64 ] || { echo "::error::the pinned wasmtime archive is x86_64; this host is $(uname -m)"; exit 1; } + if ! command -v xz >/dev/null 2>&1; then + SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo" + $SUDO apt-get update -qq + $SUDO apt-get install -y -qq xz-utils + fi + v=49.0.2 + f="wasmtime-v$v-x86_64-linux.tar.xz" + curl --proto '=https' --tlsv1.2 -sSfL -o "$RUNNER_TEMP/$f" \ + "https://github.com/bytecodealliance/wasmtime/releases/download/v$v/$f" + echo "a4d6e9e3a5a60f527cf7793d674c48930c80c2e8977995b8a275cad3254b9322 $RUNNER_TEMP/$f" | sha256sum -c - + tar -xJf "$RUNNER_TEMP/$f" -C "$RUNNER_TEMP" + echo "$RUNNER_TEMP/wasmtime-v$v-x86_64-linux" >> "$GITHUB_PATH" + # A debug build, so integer overflow panics instead of wrapping. The test + # that reads the set's files has no wasip2 build; the one that runs the + # cases from the compiled-in bundle does. + - name: The conformance set under wasmtime + shell: bash + working-directory: wai/wai-quantum + env: + CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime + run: | + set -euo pipefail + cargo test --locked --target wasm32-wasip2 --features full --test formats_conformance 2>&1 | tee "$RUNNER_TEMP/wasip2.log" + grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/wasip2.log" || { echo "::error::the conformance set did not pass under wasip2"; exit 1; } + # The same hostile inputs where an allocation larger than the address + # space panics: a release build with overflow checks on, as it is slow + # enough otherwise to outlast the job. + - name: Hostile inputs under wasmtime + shell: bash + working-directory: wai/wai-quantum + env: + CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime + CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS: "true" + run: | + set -euo pipefail + cargo test --locked --release --target wasm32-wasip2 --features full --test formats_mutation -- --nocapture 2>&1 | tee "$RUNNER_TEMP/wasip2-mutation.log" + grep -q 'test result: ok. 1 passed; 0 failed' "$RUNNER_TEMP/wasip2-mutation.log" || { echo "::error::a hostile input was not refused cleanly under wasip2"; exit 1; } + grep -q '45153 inputs: ' "$RUNNER_TEMP/wasip2-mutation.log" || { echo "::error::the count of hostile inputs under wasip2 is not the pinned 45153"; exit 1; } diff --git a/.github/workflows/wai-quantum-service.yml b/.github/workflows/wai-quantum-service.yml index d2bd69cf5..0ad4982ed 100644 --- a/.github/workflows/wai-quantum-service.yml +++ b/.github/workflows/wai-quantum-service.yml @@ -108,7 +108,7 @@ jobs: key="$RUNNER_TEMP/service.key"; rm -f "$key" "$RUNNER_TEMP/service.keys.json" store="$RUNNER_TEMP/store"; rm -rf "$store" cargo build --locked --release - WAI_QSVC_KEY="$key" WAI_QSVC_STORE="$store" WAI_QSVC_ADDR=127.0.0.1:18241 WAI_QSVC_BUDGET_GB=8 WAI_QSVC_JOB_GB=4 WAI_QSVC_WORKERS=2 \ + WAI_QSVC_KEY="$key" WAI_QSVC_STORE="$store" WAI_QSVC_ADDR=127.0.0.1:18241 WAI_QSVC_BUDGET_GB=8 WAI_QSVC_JOB_GB=4 WAI_QSVC_WORKERS=2 WAI_QSVC_CLIENT_BURST=60 \ ./target/release/wai-quantum-service serve 2> "$RUNNER_TEMP/service.log" & pid=$! trap 'kill $pid 2>/dev/null || true; cat "$RUNNER_TEMP/service.log"' EXIT @@ -168,7 +168,7 @@ jobs: key="$RUNNER_TEMP/service.key"; rm -f "$key" "$RUNNER_TEMP/service.keys.json" store="$RUNNER_TEMP/store"; rm -rf "$store" cargo build --locked --release - WAI_QSVC_KEY="$key" WAI_QSVC_STORE="$store" WAI_QSVC_ADDR=127.0.0.1:18241 WAI_QSVC_BUDGET_GB=8 WAI_QSVC_JOB_GB=4 WAI_QSVC_WORKERS=2 \ + WAI_QSVC_KEY="$key" WAI_QSVC_STORE="$store" WAI_QSVC_ADDR=127.0.0.1:18241 WAI_QSVC_BUDGET_GB=8 WAI_QSVC_JOB_GB=4 WAI_QSVC_WORKERS=2 WAI_QSVC_CLIENT_BURST=60 \ ./target/release/wai-quantum-service serve 2> "$RUNNER_TEMP/service.log" & pid=$! trap 'kill $pid 2>/dev/null || true; cat "$RUNNER_TEMP/service.log"' EXIT @@ -224,7 +224,7 @@ jobs: export CARGO_TARGET_DIR="$HOME/hw-target/wai-quantum-service" nice -n 10 cargo build --locked --release t=$(mktemp -d) - WAI_QSVC_KEY="$t/service.key" WAI_QSVC_STORE="$t/store" WAI_QSVC_ADDR=127.0.0.1:18244 WAI_QSVC_BUDGET_GB=4 WAI_QSVC_JOB_GB=2 WAI_QSVC_WORKERS=1 \ + WAI_QSVC_KEY="$t/service.key" WAI_QSVC_STORE="$t/store" WAI_QSVC_ADDR=127.0.0.1:18244 WAI_QSVC_BUDGET_GB=4 WAI_QSVC_JOB_GB=2 WAI_QSVC_WORKERS=1 WAI_QSVC_CLIENT_BURST=60 \ "$CARGO_TARGET_DIR/release/wai-quantum-service" serve 2> "$t/service.log" & pid=$! trap 'kill $pid 2>/dev/null || true; cat "$t/service.log"; rm -rf "$t"' EXIT diff --git a/.github/workflows/wai-quantum-studio.yml b/.github/workflows/wai-quantum-studio.yml index 98ab1177f..22539eef9 100644 --- a/.github/workflows/wai-quantum-studio.yml +++ b/.github/workflows/wai-quantum-studio.yml @@ -172,6 +172,7 @@ jobs: for t in an_update_is_held_to_this_build_s_key_and_the_version_it_was_signed_for only_a_copy_installed_by_its_own_installer_updates_itself the_updater_asks_the_release_s_manifest_and_requires_a_signed_version; do grep -q "^test tests::$t \.\.\. ok\$" "$RUNNER_TEMP/test.log" || { echo "::error::the update test $t did not run and pass"; exit 1; } done + grep -q '^test tests::a_program_in_another_format_opens_in_the_app \.\.\. ok$' "$RUNNER_TEMP/test.log" || { echo "::error::the formats 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 diff --git a/.github/workflows/wai-web-wasm.yml b/.github/workflows/wai-web-wasm.yml index 7c8dbd883..781946185 100644 --- a/.github/workflows/wai-web-wasm.yml +++ b/.github/workflows/wai-web-wasm.yml @@ -178,9 +178,10 @@ jobs: # engine_verdict, a repair there, the answer conformance set # (wai/quantum-conformance/answers/bundle.json) read to its codes, root and # order, its stored answers' receipts verified by running them again, a - # course check whose bytes match the native build's pinned digest, and a - # pulse run whose final density matrix is the native build's bits (its - # pinned digest). The count is the four tests above and these fifteen. + # course check whose bytes match the native build's pinned digest, a pulse + # run whose final density matrix is the native build's bits (its pinned + # digest), and a program converted between formats to the QIR bytes the + # conformance set pins. The count is the four tests above and these sixteen. - name: The job engine in a page (wasm32 tests under Node) shell: bash working-directory: wai/wai-web @@ -188,7 +189,7 @@ 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. 19 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine-wasm.log" + grep -q "test result: ok. 20 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 @@ -204,15 +205,17 @@ jobs: # sealed there and worked out again, a repair held to the flagged # lines there, an answer in the answer view layer streamed in, its calls # run and its seal verified there, a correctness record and its corpus made - # there, the circuits course read and checked there, and a pulse program's - # schedule drawn, its run as a job to the pinned bits and its check there. + # there, the circuits course read and checked there, a pulse program's + # schedule drawn, its run as a job to the pinned bits and its check there, + # and a program carried between OpenQASM, QIR and jeff there, each + # conversion checked. - 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. 27 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine.log" + grep -q "test result: ok. 28 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 diff --git a/wai/CHANGELOG.md b/wai/CHANGELOG.md index f8d8971c6..60c89c489 100644 --- a/wai/CHANGELOG.md +++ b/wai/CHANGELOG.md @@ -178,6 +178,57 @@ wire format may change and such changes are recorded here. implementation, `channel_lineup_verify.py`, from the text alone, agrees on every vector, and a CI step runs both. +### wai-quantum: QIR under its profiles and jeff, read and written, every conversion checked + +- **QIR** (`engine::formats::qir`): a module read as LLVM text, QIR 1.x (typed pointers) and 2.x (opaque pointers), + under the profile its entry point names. Under the base profile a branch, a classical instruction, reading a result, + or a gate or reset after a measurement is refused as what the profile forbids (`Q704`), on its line. Under the + adaptive profile, measurements anywhere and forward branches on results (`__quantum__rt__read_result`, or QIR 1's + `result_equal`) with the `i1` logic the profile allows are read as `if … else`, each branch to the block where its + paths meet; an optional capability the module flags do not declare is forbidden, and one they declare that the reader + does not take (loops, `switch`, `phi`, IR-defined functions, dynamic qubits or results, arrays) is refused as such + (`Q705`). Integer and float computation the flags declare is computed on reading where its operands are constants. + Rotations at any angle are read at their exact doubles, with a note (`Q709`) that the double-precision methods take + them and the bit-exact method does not; a Clifford+T module stays on the bit-exact path. A program is written as QIR + 2.0: the base profile, or the adaptive one (forward branches on results with `read_result` and `br` alone) for a + program that measures in the middle, resets or branches; Clifford+T is written as Clifford+T intrinsics and every + angle as its exact hex double. The crate's bit-exact `quantum_qir` is unchanged. +- **jeff** (`engine::formats::jeff`, format 0.3): read and written through a small Cap'n Proto reader and writer of + the crate's own, with no dependency (segments, far pointers, every element size, each pointer bounds-checked, a limit + on the words visited and the depth). The entry point's dataflow graph is run in an order its values allow: registers + extracted, inserted, sliced, split and joined by index, `for` and `while` decided on reading unrolled, a `switch` on a + measured bit read as a condition, calls inlined, classical values computed the same on every platform. Well-known + gates take their controls after their targets, as the schema orders them; a Pauli-product rotation `exp(iθP)` is + lowered exactly; a custom gate is read only by a standard gate's name (`Q711`). The writer gives one function, every + gate well-known and exact with its global phase, the bits a returned array; a program that branches is refused + (`Q720`). +- **Codes** `Q700` to `Q721` in the reader's table (`docs/qasm-diagnostics.md`): every refusal names its place (a + QIR line, a jeff function and operation). `docs/formats.md` says what each reader takes. +- **Every surface:** `wai-quantum convert FILE --to qasm|qir|jeff [--profile] [-o] [--json]`, each conversion checked + by reading the output back and holding it to the program with the equivalence ladder (a program that branches, step + for step or by every outcome's probability); every verb reads `.ll`, `.qir` and `.jeff` files, or `--from`. Both MCP + servers serve `convert_program` (`tools.v1.json#/$defs/convert`); the job service answers `POST /v1/convert`, and a + job may carry `{"source": {"format", "program" | "program_base64"}}` in place of `qasm` (`job-request.v1.json`), as + both MCP servers' run tools take it (`run_quantum_circuit`, `plan_quantum_circuit`, `run_on_target` and the dry run), + one schema and one refusal; a request whose `qasm` is what its `source` reads to is read as it is. + The Studio opens `.ll`, `.qir` and `.jeff` files through `engine_convert` (the desktop app through its `convert` + command): the program goes into the editor as OpenQASM 3, with the format, the check and each note under it, and a + refusal leaves the editor as it was with its code. +- **Hostile input:** every size, count, index and bound a file gives is held to a cap before anything is made from + it (a register's size before its qubits: a jeff register of 2^32 − 1 qubits is refused, `Q111`, where it once asked + for 34 GB and trapped in a page); the wire reader bounds every pointer in 64 bits and counts each list element + against the words it visits; the jeff reader counts every register slot, array element, call frame and condition + term against one budget; the QIR reader bounds its branches' nesting, the instructions it runs over every path and + the state each path copies; a condition is at most 1 024 terms and 32 deep once written. A conversion's check is + not run past 262 144 gates applied or 4·10⁹ amplitudes, and says so. `tests/formats_mutation.rs` mutates every file + of the conformance set from a fixed seed (45 153 inputs: bits flipped, files cut and spliced, every length, count, + offset and 32-bit field inflated) and holds each to converting or a refusal with a code, within 20 s and 1 GiB, + natively and under wasm32-wasip2 in `wai-quantum-formats.yml`. +- **Conformance** (`wai/quantum-conformance/formats/`): the QIR specification's two examples, every intrinsic, a QIR 1 + module, each profile's refusals, hand-built jeff modules, the jeff project's own structured benchmarks and examples + unchanged, and OpenQASM written to each format with the QIR bytes pinned, run natively and under wasm32-wasip2 + (`wai-quantum-formats.yml`). `wai-web-wasm.yml`'s pins move to 19 (wasm32) and 27 (native). + ### wai-quantum: pulse and dynamics, simulated, the same bits everywhere, its receipts binding the calibrations - **OpenPulse read** (`qasm::pulse`): a pulse program's `cal` and `defcal` blocks on physical qubits run in diff --git a/wai/quantum-conformance/README.md b/wai/quantum-conformance/README.md index b28ba1d0d..f87283d9e 100644 --- a/wai/quantum-conformance/README.md +++ b/wai/quantum-conformance/README.md @@ -20,7 +20,10 @@ The OpenQASM 3 reader's corpus, programs with what the reader must do with each, is `qasm3/`, with a README of its own. The error-correction text formats (noisy Clifford circuits and detector error models, read and written back byte for byte, with what each must count and what must be refused) are `qec-text/`, -with a README of its own. +with a README of its own. Programs in other formats (QIR under its profiles, +and jeff), each with the program it must read to, its round trip and its +refusals, and OpenQASM written to each, are `formats/`, 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 diff --git a/wai/quantum-conformance/formats/README.md b/wai/quantum-conformance/formats/README.md new file mode 100644 index 000000000..1329bfb35 --- /dev/null +++ b/wai/quantum-conformance/formats/README.md @@ -0,0 +1,63 @@ +# Other formats: conformance + +Programs in QIR and jeff, each with what the engine's readers and writers +(`wai_quantum::engine::formats`, [docs](../../wai-quantum/docs/formats.md)) +must do with it: read it to the program a reference gives, give the outcomes +it must give, write it back to its own format and read it again unchanged, +or refuse it with a code and its place. OpenQASM sources are written to each +format and read back. + +| Path | What | +|---|---| +| `qir/` | QIR modules: the specification's two example programs (the base profile's Bell pair and the adaptive profile's teleport chain, MIT, as the specification publishes its code), every intrinsic the reader takes, a Clifford+T module, a QIR 1 module with typed pointers from the crate's earlier writer, computation the module flags declare, and a module that names no profile. `*.ref.qasm` are the references, written by hand. | +| `qir/refuse/` | A module for each thing a profile forbids, each capability declared and not taken, a stale read, an unknown intrinsic, a qubit past its count, an unknown profile, QIR 3, and text that is not LLVM: each with its code and line. | +| `jeff/` | jeff modules built field by field from the schema (not by the writer): well-known gates with their controls after their targets, custom gates named as standard gates, a Pauli-product rotation; `refuse/` the refusals. A unit test (`engine::formats::tests::the_conformance_set_holds_these_modules`) makes them and holds the files to them. | +| `jeff/upstream/` | The jeff project's own structured benchmarks (GHZ, QFT, QPE, Grover, teleportation, iterative QFT and QPE, multiplexer, QFT adders, a controlled modular multiplier: loops over registers, switches on measured bits, several segments) and its examples, unchanged (Apache-2.0), from the jeff repository at commit `97f73829caa2de0b6fbf925d1f97f9eb10c158ad`; the examples renamed by what they show. `*.ref.qasm` are references written by hand from each benchmark's source. Those the readers cannot hold exactly are refused with their codes. | +| `qasm/` | OpenQASM sources written to QIR and jeff and read back: every gate of the language, Clifford+T, a branching program, a measurement in the middle with a reset, and timing neither format holds. The QIR written is pinned by its BLAKE3, the same bytes natively and under wasm32-wasip2. | +| `manifest.json` | Each case: its file, its format, what it covers, its reference, and its expectations (the manifest's `expectations` says what each kind means). | +| `bundle.json` | The manifest's cases with each file inline (text, or base64 for jeff), for builds that read no files. | + +## Native + +```sh +cd wai/wai-quantum +cargo test --release --features full --test formats_conformance +cargo test --release --features full --lib engine::formats +``` + +Two tests: every case against its expectations, and the bundle held equal to +the files. After changing a file or the manifest, write the bundle: + +```sh +WAI_FORMATS_BUNDLE_WRITE=1 cargo test --release --features full --test formats_conformance the_bundle_is_the_corpus +``` + +and after changing the hand-built jeff modules, +`WAI_FORMATS_VECTORS_WRITE=1 cargo test --release --features full --lib the_conformance_set_holds_these_modules`. + +The same files are the seed of the hostile-input test: every QIR and jeff +file mutated (bits flipped, cut short, spliced with another, and every +length, count, offset and 32-bit field inflated), 45 153 inputs from a fixed +seed, each converted or refused with a code within 20 s and 1 GiB: + +```sh +cargo test --release --features full --test formats_mutation -- --nocapture +``` + +## WASI (the edge) + +```sh +CARGO_TARGET_WASM32_WASIP2_RUNNER=wasmtime \ + cargo test --target wasm32-wasip2 --features full --test formats_conformance +``` + +The cases come from the bundle compiled into the test. The hostile-input +test runs there too, as a release build with overflow checks on: + +```sh +CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS=true CARGO_TARGET_WASM32_WASIP2_RUNNER=wasmtime \ + cargo test --release --target wasm32-wasip2 --features full --test formats_mutation +``` + +CI: `wai-quantum-formats.yml` (the readers' and writers' unit tests, and the +set natively and under wasm32-wasip2). diff --git a/wai/quantum-conformance/formats/bundle.json b/wai/quantum-conformance/formats/bundle.json new file mode 100644 index 000000000..89216dc98 --- /dev/null +++ b/wai/quantum-conformance/formats/bundle.json @@ -0,0 +1,718 @@ +{ + "format": "wai.formats-conformance/1", + "about": "The manifest's cases with each file inline (text, or base64 for jeff), for builds that read no files.", + "cases": [ + { + "file": "qir/bell_base.ll", + "format": "qir", + "expect": { + "profile": "base", + "notes": [], + "how": "tableau", + "outcomes": [ + "00", + "11" + ], + "round_trip": true + }, + "text": "; The base profile's own example program (the QIR specification, MIT): two\n; qubits entangled, measured, and a tuple of the two results recorded.\n\n@0 = internal constant [3 x i8] c\"r1\\00\"\n@1 = internal constant [3 x i8] c\"r2\\00\"\n@2 = internal constant [3 x i8] c\"t0\\00\"\n\ndefine i64 @Entry_Point_Name() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\n\nbody: ; preds = %entry\n call void @__quantum__qis__h__body(ptr null)\n call void @__quantum__qis__cnot__body(ptr null, ptr inttoptr (i64 1 to ptr))\n br label %measurements\n\nmeasurements: ; preds = %body\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr))\n br label %output\n\noutput: ; preds = %measurements\n call void @__quantum__rt__tuple_record_output(i64 2, ptr @2)\n call void @__quantum__rt__result_record_output(ptr null, ptr @0)\n call void @__quantum__rt__result_record_output(ptr inttoptr (i64 1 to ptr), ptr @1)\n\n ret i64 0\n}\n\ndeclare void @__quantum__qis__h__body(ptr)\n\ndeclare void @__quantum__qis__cnot__body(ptr, ptr)\n\ndeclare void @__quantum__qis__mz__body(ptr, ptr writeonly) #1\n\ndeclare void @__quantum__rt__initialize(ptr)\n\ndeclare void @__quantum__rt__tuple_record_output(i64, ptr)\n\ndeclare void @__quantum__rt__result_record_output(ptr, ptr)\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"schema_id\" \"required_num_qubits\"=\"2\" \"required_num_results\"=\"2\" }\n\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + { + "file": "qir/every_intrinsic.ll", + "format": "qir", + "expect": { + "profile": "base", + "notes": [ + "Q709" + ], + "how": "exact", + "round_trip": true + }, + "text": "; Every intrinsic the reader takes, once each, at angles that are not dyadic where an intrinsic takes one (decimal, exponent and hex), then every qubit measured.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__h__body(ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__x__body(ptr null)\n call void @__quantum__qis__y__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__z__body(ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__s__body(ptr null)\n call void @__quantum__qis__s__adj(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__t__body(ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__t__adj(ptr null)\n call void @__quantum__qis__sx__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__rx__body(double 3.000000e-01, ptr null)\n call void @__quantum__qis__ry__body(double 0xBFF199999999999A, ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__rz__body(double 2.5, ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__rz__adj(double 0.7, ptr null)\n call void @__quantum__qis__r1__body(double 0.9, ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__cnot__body(ptr null, ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__cx__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__cy__body(ptr inttoptr (i64 2 to ptr), ptr null)\n call void @__quantum__qis__cz__body(ptr null, ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__swap__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__ccx__body(ptr null, ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__ccz__body(ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 1 to ptr), ptr null)\n call void @__quantum__qis__rxx__body(double 0.4, ptr null, ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__ryy__body(double -0.6, ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr))\n call void @__quantum__qis__rzz__body(double 1.3, ptr inttoptr (i64 2 to ptr), ptr null)\n br label %measurements\nmeasurements:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) #1\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr)) #1\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"3\" \"required_num_results\"=\"3\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0]; h q[1]; h q[2];\nx q[0]; y q[1]; z q[2]; s q[0]; sdg q[1]; t q[2]; tdg q[0]; sx q[1];\nrx(0.3) q[0]; ry(-1.1) q[1]; rz(2.5) q[2]; rz(-0.7) q[0]; p(0.9) q[1];\ncx q[0], q[1]; cx q[1], q[2]; cy q[2], q[0]; cz q[0], q[2]; swap q[1], q[2]; ccx q[0], q[1], q[2]; ctrl(2) @ z q[2], q[1], q[0];\nrxx(0.4) q[0], q[1]; ryy(-0.6) q[1], q[2]; rzz(1.3) q[2], q[0];\nc = measure q;\n" + }, + { + "file": "qir/clifford_t.ll", + "format": "qir", + "expect": { + "profile": "base", + "notes": [], + "how": "exact", + "exact": true, + "round_trip": true + }, + "text": "; A Clifford+T module, one rotation by exactly pi/4 in hex: it reads with no note and runs on the bit-exact method.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n call void @__quantum__qis__t__body(ptr null)\n call void @__quantum__qis__cx__body(ptr null, ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__s__adj(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, ptr inttoptr (i64 1 to ptr))\n br label %measurements\nmeasurements:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) #1\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"2\" \"required_num_results\"=\"2\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nsdg q[1];\nh q[1];\nrz(pi/4) q[1];\nc = measure q;\n" + }, + { + "file": "qir/qft3_typed.ll", + "format": "qir", + "expect": { + "profile": "base", + "notes": [], + "how": "exact", + "exact": true, + "round_trip": true + }, + "text": "; QIR emitted by wai-quantum — deterministic, byte-identical on every target.\nsource_filename = \"wai_quantum\"\n\n%Qubit = type opaque\n%Result = type opaque\n\ndeclare void @__quantum__qis__h__body(%Qubit*)\ndeclare void @__quantum__qis__rz__body(double, %Qubit*)\ndeclare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*)\ndeclare void @__quantum__qis__mz__body(%Qubit*, %Result*)\ndeclare void @__quantum__rt__result_record_output(%Result*, i8*)\n\ndefine void @main() #0 {\nentry:\n call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0xBFE921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FD921FB54442D18, %Qubit* inttoptr (i64 2 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0xBFD921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FD921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 2 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0xBFE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*))\n call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*))\n call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*))\n call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))\n call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))\n call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*))\n call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)\n call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)\n call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* null)\n ret void\n}\n\nattributes #0 = { \"entry_point\" \"output_labeling_schema\" \"qir_profiles\"=\"base_profile\" \"required_num_qubits\"=\"3\" \"required_num_results\"=\"3\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 1}\n!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[0];\ncp(2*pi/4) q[1],q[0];\ncp(2*pi/8) q[2],q[0];\nh q[1];\ncp(2*pi/4) q[2],q[1];\nh q[2];\ncx q[0],q[2];\ncx q[2],q[0];\ncx q[0],q[2];\n" + }, + { + "file": "qir/teleport_chain.ll", + "format": "qir", + "expect": { + "profile": "adaptive", + "notes": [], + "dynamic": true, + "qubits": 6, + "equal_bits": [ + [ + 4, + 5 + ] + ], + "round_trip": true + }, + "text": "; The adaptive profile's own example program (the QIR specification, MIT): a\n; Bell pair whose second half is teleported twice, each step measuring in\n; the middle and correcting on the result. The pair ends between qubits 0 and\n; 5, so results 4 and 5 are equal on every shot.\n\n@0 = internal constant [5 x i8] c\"0_t0\\00\"\n@1 = internal constant [5 x i8] c\"0_t1\\00\"\n\ndefine i64 @TeleportChain() local_unnamed_addr #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\n\nbody: ; preds = %entry\n tail call void @__quantum__qis__h__body(ptr null)\n tail call void @__quantum__qis__cnot__body(ptr null, ptr nonnull inttoptr (i64 1 to ptr))\n tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 2 to ptr))\n tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 2 to ptr), ptr nonnull inttoptr (i64 4 to ptr))\n tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 3 to ptr))\n tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 3 to ptr), ptr nonnull inttoptr (i64 5 to ptr))\n tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 1 to ptr), ptr nonnull inttoptr (i64 2 to ptr))\n tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 1 to ptr))\n tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 1 to ptr), ptr writeonly null)\n tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 1 to ptr))\n %0 = tail call i1 @__quantum__rt__read_result(ptr readonly null)\n br i1 %0, label %then__1, label %continue__1\n\n; conditional quantum gate (only one in this block, but many can appear and the full quantum instruction set should be usable)\nthen__1: ; preds = %body\n tail call void @__quantum__qis__z__body(ptr nonnull inttoptr (i64 4 to ptr))\n br label %continue__1\n\ncontinue__1: ; preds = %then__1, %body\n tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 2 to ptr), ptr writeonly nonnull inttoptr (i64 1 to ptr))\n tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 2 to ptr))\n %1 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 1 to ptr))\n br i1 %1, label %then__2, label %continue__2\n\nthen__2: ; preds = %continue__1\n tail call void @__quantum__qis__x__body(ptr nonnull inttoptr (i64 4 to ptr))\n br label %continue__2\n\ncontinue__2: ; preds = %then__2, %continue__1\n tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 4 to ptr), ptr nonnull inttoptr (i64 3 to ptr))\n tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 4 to ptr))\n tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 4 to ptr), ptr writeonly nonnull inttoptr (i64 2 to ptr))\n tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 4 to ptr))\n %2 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 2 to ptr))\n br i1 %2, label %then__3, label %continue__3\n\nthen__3: ; preds = %continue__2\n tail call void @__quantum__qis__z__body(ptr nonnull inttoptr (i64 5 to ptr))\n br label %continue__3\n\ncontinue__3: ; preds = %then__3, %continue__2\n tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 3 to ptr), ptr writeonly nonnull inttoptr (i64 3 to ptr))\n tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 3 to ptr))\n %3 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 3 to ptr))\n br i1 %3, label %then__4, label %continue__4\n\nthen__4: ; preds = %continue__3\n tail call void @__quantum__qis__x__body(ptr nonnull inttoptr (i64 5 to ptr))\n br label %continue__4\n\ncontinue__4: ; preds = %continue__3, %then__4\n tail call void @__quantum__qis__mz__body(ptr null, ptr writeonly nonnull inttoptr (i64 4 to ptr))\n tail call void @__quantum__qis__reset__body(ptr null)\n tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 5 to ptr), ptr writeonly nonnull inttoptr (i64 5 to ptr))\n tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 5 to ptr))\n br label %exit\n\nexit:\n call void @__quantum__rt__result_record_output(ptr nonnull inttoptr (i64 4 to ptr), ptr @0)\n call void @__quantum__rt__result_record_output(ptr nonnull inttoptr (i64 5 to ptr), ptr @1)\n ret i64 0\n}\n\n; declarations of QIS functions\n\ndeclare void @__quantum__qis__cnot__body(ptr, ptr) local_unnamed_addr\n\ndeclare void @__quantum__qis__h__body(ptr) local_unnamed_addr\n\ndeclare void @__quantum__qis__x__body(ptr) local_unnamed_addr\n\ndeclare void @__quantum__qis__z__body(ptr) local_unnamed_addr\n\ndeclare void @__quantum__qis__reset__body(ptr) local_unnamed_addr\n\ndeclare void @__quantum__qis__mz__body(ptr, ptr writeonly) #1\n\n; declarations of runtime functions\n\ndeclare void @__quantum__rt__initialize(ptr)\n\ndeclare i1 @__quantum__rt__read_result(ptr readonly)\n\ndeclare void @__quantum__rt__result_record_output(ptr, ptr)\n\n; attributes\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"schema_id\" \"required_num_qubits\"=\"6\" \"required_num_results\"=\"6\" }\n\nattributes #1 = { \"irreversible\" }\n\n; module flags\n\n!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9}\n\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n!4 = !{i32 5, !\"int_computations\", !10}\n!5 = !{i32 5, !\"float_computations\", !11}\n!6 = !{i32 1, !\"ir_functions\", i1 false}\n!7 = !{i32 1, !\"backwards_branching\", i2 0}\n!8 = !{i32 1, !\"multiple_target_branching\", i1 false}\n!9 = !{i32 1, !\"multiple_return_points\", i1 false}\n!10 = !{!\"i32\", !\"i64\"}\n!11 = !{!\"float\", !\"double\"}\n" + }, + { + "file": "qir/computed.ll", + "format": "qir", + "expect": { + "profile": "adaptive", + "notes": [ + "Q709" + ], + "dynamic": true, + "outcomes": [ + "001", + "010", + "100", + "111" + ], + "round_trip": true + }, + "text": "; Adaptive: an angle computed from constants under float_computations, and a branch on two results combined by i1 logic: result 2 is 1 exactly when results 0 and 1 agree.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n %a = fadd double 0.25, 0.5\n %b = fmul double %a, 2.0\n call void @__quantum__qis__ry__body(double %b, ptr null)\n call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr))\n %r0 = call i1 @__quantum__rt__read_result(ptr readonly null)\n %r1 = call i1 @__quantum__rt__read_result(ptr readonly inttoptr (i64 1 to ptr))\n %x = xor i1 %r0, %r1\n %n = xor i1 %x, true\n br i1 %n, label %same, label %out\nsame:\n call void @__quantum__qis__x__body(ptr inttoptr (i64 2 to ptr))\n br label %out\nout:\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr))\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"3\" \"required_num_results\"=\"3\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3, !4, !5}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n!4 = !{i32 5, !\"float_computations\", !10}\n!5 = !{i32 1, !\"backwards_branching\", i2 0}\n!10 = !{!\"double\"}\n" + }, + { + "file": "qir/no_profile.ll", + "format": "qir", + "expect": { + "profile": "base", + "notes": [ + "Q702" + ], + "outcomes": [ + "0", + "1" + ] + }, + "text": "; A module that names no profile: read under the profile its instructions need (base), and said so.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n br label %measurements\nmeasurements:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/base_branch.ll", + "format": "qir", + "expect": { + "refused": "Q704", + "line": 12 + }, + "text": "; Under the base profile, reading a result: forbidden.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n br label %measurements\nmeasurements:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/base_gate_after_measure.ll", + "format": "qir", + "expect": { + "refused": "Q704", + "line": 11 + }, + "text": "; Under the base profile, a gate on a qubit after its measurement: forbidden.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1\n br label %measurements\nmeasurements:\n call void @__quantum__qis__x__body(ptr null)\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/undeclared_float.ll", + "format": "qir", + "expect": { + "refused": "Q704", + "line": 6 + }, + "text": "; The same computation with no float_computations flag: what the adaptive profile forbids, refused on its line.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n %a = fadd double 0.25, 0.5\n %b = fmul double %a, 2.0\n call void @__quantum__qis__ry__body(double %b, ptr null)\n call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr))\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr))\n %r0 = call i1 @__quantum__rt__read_result(ptr readonly null)\n %r1 = call i1 @__quantum__rt__read_result(ptr readonly inttoptr (i64 1 to ptr))\n %x = xor i1 %r0, %r1\n %n = xor i1 %x, true\n br i1 %n, label %same, label %out\nsame:\n call void @__quantum__qis__x__body(ptr inttoptr (i64 2 to ptr))\n br label %out\nout:\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr))\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"3\" \"required_num_results\"=\"3\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/loop_undeclared.ll", + "format": "qir", + "expect": { + "refused": "Q704", + "line": 10 + }, + "text": "; A loop (a branch back to an earlier block) with no backwards_branching flag: forbidden.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %l\nl:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n br i1 %r, label %l, label %done\ndone:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/loop_declared.ll", + "format": "qir", + "expect": { + "refused": "Q705", + "line": 10 + }, + "text": "; The same loop with backwards_branching declared: a capability this reader does not take.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %l\nl:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n br i1 %r, label %l, label %done\ndone:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3, !4}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n!4 = !{i32 1, !\"backwards_branching\", i2 2}\n" + }, + { + "file": "qir/refuse/switch_declared.ll", + "format": "qir", + "expect": { + "refused": "Q705", + "line": 8 + }, + "text": "; A switch on a result, multiple-target branching declared: a capability this reader does not take.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n switch i1 %r, label %done [ i1 true, label %one ]\none:\n call void @__quantum__qis__x__body(ptr null)\n br label %done\ndone:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3, !4}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n!4 = !{i32 1, !\"multiple_target_branching\", i1 true}\n" + }, + { + "file": "qir/refuse/dynamic_qubits.ll", + "format": "qir", + "expect": { + "refused": "Q705", + "line": 6 + }, + "text": "; A qubit allocated at run time, with dynamic qubit management declared: a capability this reader does not take.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n %q = call ptr @__quantum__rt__qubit_allocate()\n call void @__quantum__qis__h__body(ptr %q)\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"0\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 true}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/stale_read.ll", + "format": "qir", + "expect": { + "refused": "Q708", + "line": 9 + }, + "text": "; A result read, measured again into the same result, and only then branched on: the branch would read the later measurement.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly null)\n br i1 %r, label %a, label %b\na:\n call void @__quantum__qis__x__body(ptr inttoptr (i64 1 to ptr))\n br label %b\nb:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"2\" \"required_num_results\"=\"1\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/unknown_intrinsic.ll", + "format": "qir", + "expect": { + "refused": "Q706", + "line": 8 + }, + "text": "; An intrinsic the reader does not know.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__foo__body(ptr null)\n br label %measurements\nmeasurements:\n\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"0\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/qubit_past_count.ll", + "format": "qir", + "expect": { + "refused": "Q707", + "line": 8 + }, + "text": "; Qubit 4 in a module that declares one qubit.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr inttoptr (i64 4 to ptr))\n br label %measurements\nmeasurements:\n\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"0\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/unknown_profile.ll", + "format": "qir", + "expect": { + "refused": "Q703", + "line": 3 + }, + "text": "; A profile the reader does not take.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n br label %measurements\nmeasurements:\n\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"custom_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"0\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 2}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/major_version.ll", + "format": "qir", + "expect": { + "refused": "Q701" + }, + "text": "; A QIR major version the reader does not take.\n\ndefine i64 @main() #0 {\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n call void @__quantum__qis__h__body(ptr null)\n br label %measurements\nmeasurements:\n\n br label %output\noutput:\n ret i64 0\n}\n\nattributes #0 = { \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"labels\" \"required_num_qubits\"=\"1\" \"required_num_results\"=\"0\" }\nattributes #1 = { \"irreversible\" }\n\n!llvm.module.flags = !{!0, !1, !2, !3}\n!0 = !{i32 1, !\"qir_major_version\", i32 3}\n!1 = !{i32 7, !\"qir_minor_version\", i32 1}\n!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n" + }, + { + "file": "qir/refuse/not_llvm.ll", + "format": "qir", + "expect": { + "refused": "Q700" + }, + "text": "; Not LLVM text: no function, no entry point.\nOPENQASM 3.0;\nqubit q;\n" + }, + { + "file": "jeff/controls_after_targets.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q709" + ], + "how": "exact", + "round_trip": true + }, + "base64": "AAAAAAABAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAOUDAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAlQIAADcBAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAhwIAAAAAAAAAAAAAQAAAAAEABAAAAAAAAAAAADkBAAAEAAAANQEAAAwAAAAAAAAAAAAAADABAAABAAEAAAAAAAAAAAAxAQAABAAAAC0BAAAMAAAAAAAAAAAAAAAoAQAAAQABAAAAAAAAAAAAKQEAAAQAAAAlAQAADAAAAAAAAAAAAAAAIAEAAAEAAQAAAAAAAAAAACEBAAAMAAAAIQEAAAwAAAAAAAAAAAAAABwBAAABAAEAAAAAAAAAAAApAQAAFAAAACkBAAAUAAAAAAAAAAAAAAAkAQAAAQABAAQAAAAAAAAAMQEAAAQAAAAtAQAADAAAAAAAAAAAAAAAKAEAAAIAAAAAAAAAAAAAACkBAAAUAAAAKQEAAAwAAAAAAAAAAAAAACQBAAABAAEAAAAAAAAAAAAxAQAADAAAADEBAAAMAAAAAAAAAAAAAAAsAQAAAQABAAQAAAAAAAAAOQEAAAQAAAA1AQAADAAAAAAAAAAAAAAAMAEAAAIAAAAEAAAAAAAAADEBAAAEAAAALQEAAAwAAAAAAAAAAAAAACgBAAACAAAABAAAAAAAAAApAQAABAAAACUBAAAMAAAAAAAAAAAAAAAgAQAAAgAAAAAAAAAAAAAAIQEAACQAAAAlAQAADAAAAAAAAAAAAAAAIAEAAAEAAQAEAAAAAAAAAC0BAAAEAAAAKQEAAAwAAAAAAAAAAAAAACQBAAACAAAAAAAAAAAAAAAlAQAAJAAAACkBAAAcAAAAAAAAAAAAAAAoAQAAAQABAAQAAAAAAAAANQEAAAQAAAAxAQAADAAAAAAAAAAAAAAALAEAAAIAAAAAAAAAAAAAAC0BAAAUAAAALQEAAAwAAAAAAAAAAAAAACgBAAABAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAwAAAAQAAAAFAAAABgAAAAAAAAAAAAAAAgABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAEAAAAzMzMzMzPTPwIAAAAGAAAABwAAAAAAAAAGAAAAAAAAAAAAAAACAAEABgAAAAAAAAEDAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAIAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQADAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAJAAAAAAAAAAAAAAABAAAAmpmZmZmZyT8KAAAAAAAAAAAAAAABAAAAmpmZmZmZ2T8LAAAAAAAAAAAAAAABAAAAMzMzMzMz478EAAAACQAAAAoAAAALAAAADAAAAAAAAAAGAAAAAAAAAAAAAAACAAEACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQAAAAAAAAAAAAAAAQAAAM3MzMzMzOw/BwAAAAgAAAAMAAAADQAAAA4AAAAPAAAAEAAAAAAAAAAGAAAAAAAAAAAAAAACAAEABQAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAQAAAAAAAAAAANA/DwAAABEAAAASAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQANAAAAAAABAAAAAAAAAAAAAAAAAAAAAABMAAAAAAACAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAACUAAAAAgAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACoAAABtYWluAAAAAA==", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[0];\ncx q[0], q[1];\nrx(-0.9) q[2];\nsdg q[0];\nU(0.2, 0.4, -0.6) q[1];\nctrl(2) @ p(0.9) q[0], q[1], q[2];\np(0.25) q[0];\n" + }, + { + "file": "jeff/custom_named.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q709", + "Q711" + ], + "how": "exact", + "round_trip": true + }, + "base64": "AAAAAHYAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAJkBAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQACQEAAHcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAA9wAAAAAAAAAAAAAAGAAAAAEABAAAAAAAAAAAAHEAAAAEAAAAbQAAAAwAAAAAAAAAAAAAAGgAAAABAAEAAAAAAAAAAABpAAAABAAAAGUAAAAMAAAAAAAAAAAAAABgAAAAAQABAAAAAAAAAAAAYQAAAAwAAABhAAAADAAAAAAAAAAAAAAAXAAAAAEAAQAAAAAAAAAAAGkAAAAUAAAAaQAAABQAAAAAAAAAAAAAAGQAAAABAAEABAAAAAAAAABxAAAABAAAAG0AAAAMAAAAAAAAAAAAAABoAAAAAgAAAAAAAAAAAAAAaQAAABQAAABpAAAADAAAAAAAAAAAAAAAZAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABgAAAAAAAAAAAAAAAgABAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAABAAAAAwAAAAQAAAAGAAAAAAAAAAAAAAACAAEAAgABAAIAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAQAAAAAAAAAAAOA/BAAAAAUAAAAGAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQADAAEAAQEAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAACADQAAAACAAAAAAAAAAAAAAA0AAAAAgAAAAAAAAAAAAAANAAAAAIAAAAAAAAAAAAAADQAAAACAAAAAAAAAAAAAAA0AAAAAgAAAAAAAAAAAAAANAAAAAIAAAAAAAAAAAAAADQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAACoAAAARAAAAEgAAABEAAAAaAAAAEQAAABoAAAARAAAASgAAAG1haW4AAAAASAAAAAAAAABDWAAAAAAAAFJ4AAAAAAAASGFkYW1hcmQAAAAAAAAAAA==", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nh q[0];\ncx q[0], q[1];\nrx(0.5) q[1];\n" + }, + { + "file": "jeff/pauli_product.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q709", + "Q712" + ], + "how": "exact", + "round_trip": true + }, + "base64": "AAAAAGAAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAGUBAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQA1QAAAHcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAzwAAAAAAAAAAAAAAFAAAAAEABAAAAAAAAAAAAF0AAAAEAAAAWQAAAAwAAAAAAAAAAAAAAFQAAAABAAEAAAAAAAAAAABVAAAABAAAAFEAAAAMAAAAAAAAAAAAAABMAAAAAQABAAAAAAAAAAAATQAAAAQAAABJAAAADAAAAAAAAAAAAAAARAAAAAEAAQAEAAAAAAAAAEUAAAAEAAAAQQAAAAwAAAAAAAAAAAAAADwAAAACAAAAAAAAAAAAAAA9AAAAJAAAAEEAAAAcAAAAAAAAAAAAAABAAAAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAEAAACuR+F6FK7XPwAAAAABAAAAAgAAAAMAAAAEAAAABQAAAAYAAAAAAAAABgAAAAAAAAAAAAAAAgABAAAAAgAAAAAAAAAAAAAAAAABAAAAGwAAAAEAAgADAAAAHAAAAAAAAgA0AAAAAgAAAAAAAAAAAAAANAAAAAIAAAAAAAAAAAAAADQAAAACAAAAAAAAAAAAAAA0AAAAAgAAAAAAAAAAAAAANAAAAAIAAAAAAAAAAAAAADQAAAACAAAAAAAAAAAAAAA0AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAqAAAAbWFpbgAAAAA=", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// exp(0.37i X⊗Y⊗Z), with its parity carried along a chain rather than gathered on the last qubit.\nqubit[3] q;\nh q[0];\nsdg q[1];\nh q[1];\ncx q[0], q[1];\ncx q[1], q[2];\nrz(-0.74) q[2];\ncx q[1], q[2];\ncx q[0], q[1];\nh q[1];\ns q[1];\nh q[0];\n" + }, + { + "file": "jeff/refuse/custom_unknown.jeff", + "format": "jeff", + "expect": { + "refused": "Q710", + "at": "operation 1" + }, + "base64": "AAAAADoAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAKkAAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAaQAAACcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAVwAAAAAAAAAAAAAACAAAAAEABAAAAAAAAAAAACEAAAAEAAAAHQAAAAwAAAAAAAAAAAAAABgAAAABAAEAAAAAAAAAAAAZAAAADAAAABkAAAAMAAAAAAAAAAAAAAAUAAAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAAAAAACAAEABAABAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAgAMAAAAAgAAAAAAAAAAAAAADAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAACoAAAARAAAAEgAAABEAAAAaAAAAEQAAABoAAAARAAAASgAAAG1haW4AAAAASAAAAAAAAABDWAAAAAAAAFJ4AAAAAAAASGFkYW1hcmQAAAAAAAAAAA==" + }, + { + "file": "jeff/refuse/custom_wrong_qubits.jeff", + "format": "jeff", + "expect": { + "refused": "Q710", + "at": "operation 1" + }, + "base64": "AAAAADoAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAKkAAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAaQAAACcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAVwAAAAAAAAAAAAAACAAAAAEABAAAAAAAAAAAACEAAAAEAAAAHQAAAAwAAAAAAAAAAAAAABgAAAABAAEAAAAAAAAAAAAZAAAADAAAABkAAAAMAAAAAAAAAAAAAAAUAAAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAAAAAACAAEAAgABAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAgAMAAAAAgAAAAAAAAAAAAAADAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAACoAAAARAAAAEgAAABEAAAAaAAAAEQAAABoAAAARAAAASgAAAG1haW4AAAAASAAAAAAAAABDWAAAAAAAAFJ4AAAAAAAASGFkYW1hcmQAAAAAAAAAAA==" + }, + { + "file": "jeff/refuse/qubit_twice.jeff", + "format": "jeff", + "expect": { + "refused": "Q713", + "at": "operation 2" + }, + "base64": "AAAAAEEAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAOkAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAmQAAADcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAfwAAAAAAAAAAAAAADAAAAAEABAAAAAAAAAAAADUAAAAEAAAAMQAAAAwAAAAAAAAAAAAAACwAAAABAAEAAAAAAAAAAAAtAAAADAAAAC0AAAAMAAAAAAAAAAAAAAAoAAAAAQABAAAAAAAAAAAANQAAAAwAAAA1AAAADAAAAAAAAAAAAAAAMAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAAAAAAAACAAEACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAgAUAAAAAgAAAAAAAAAAAAAAFAAAAAIAAAAAAAAAAAAAABQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAKgAAAG1haW4AAAAA" + }, + { + "file": "jeff/refuse/old_version.jeff", + "format": "jeff", + "expect": { + "refused": "Q701" + }, + "base64": "AAAAACEAAAAAAAAAAgAFAAAAAAAAAAAAAQAAAAAAAAARAAAAJwAAAGkAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAOQAAABcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAALwAAAAAAAAAAAAAABAAAAAEABAAAAAAAAAAAAA0AAAAEAAAACQAAAAwAAAAAAAAAAAAAAAQAAAABAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAgAEAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAqAAAAbWFpbgAAAAA=" + }, + { + "file": "jeff/refuse/angle_from_measurement.jeff", + "format": "jeff", + "expect": { + "refused": "Q714", + "at": "operation 4" + }, + "base64": "AAAAAGcAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAIEBAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAAQEAAGcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAA9wAAAAAAAAAAAAAAGAAAAAEABAAAAAAAAAAAAHEAAAAEAAAAbQAAAAwAAAAAAAAAAAAAAGgAAAABAAEAAAAAAAAAAABpAAAABAAAAGUAAAAMAAAAAAAAAAAAAABgAAAAAQABAAAAAAAAAAAAYQAAAAwAAABhAAAADAAAAAAAAAAAAAAAXAAAAAEAAQAAAAAAAAAAAGkAAAAMAAAAaQAAAAwAAAAAAAAAAAAAAGQAAAABAAEAAgAAAAAAAABlAAAADAAAAGUAAAAMAAAAAAAAAAAAAABgAAAAAgAAAAAAAAAAAAAAYQAAABQAAABhAAAADAAAAAAAAAAAAAAAXAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAABAAAABAAAAAUAAAAAAAAABgAAAAAAAAAAAAAAAgABAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAIALAAAAAIAAAAAAAAAAAAAACwAAAACAAAAAAAAAAAAAAAsAAAAAgAAAAAAAAAAAAAALAAAAAIAAAAAAAAAAAAAACwAAAACAAAAAAAAAAAAAAAsAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACoAAABtYWluAAAAAA==" + }, + { + "file": "jeff/refuse/truncated.jeff", + "format": "jeff", + "expect": { + "refused": "Q700" + }, + "base64": "AAAAAHYAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAJkBAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQACQEAAHcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAA9wAAAAAAAAAAAAAAGAAAAAEABAAAAAAAAAAAAHEAAAAEAAAAbQAAAAwAAAAAAAAAAAAAAGgAAAABAAEAAAAAAAAAAAA=" + }, + { + "file": "jeff/refuse/register_too_large.jeff", + "format": "jeff", + "expect": { + "refused": "Q111", + "at": "operation 1" + }, + "base64": "AAAAAC0AAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAJkAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAWQAAACcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAAVwAAAAAAAAAAAAAACAAAAAEABAACAAAAAAAAACEAAAAEAAAAHQAAAAwAAAAAAAAAAAAAABgAAAACAAAAAQAAAAAAAAAZAAAADAAAABkAAAAMAAAAAAAAAAAAAAAUAAAAAQAAAAAAAAAAAAAAAAADAP////8AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAgAAAAAAAIADAAAAAIAAAAAAAAAAAAAAAwAAAACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAqAAAAbWFpbgAAAAA=" + }, + { + "file": "jeff/upstream/ghz-linear_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "how": "tableau", + "outcomes": [ + "000", + "111" + ], + "round_trip": true + }, + "base64": "AAAAAHABAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAACdBQAAOgAAAJ0FAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAF0DAAAnAgAAAAAAAAAAAAANAAAABAAAAE0DAAAMAAAABQAAAL8BAAAAAAAAAAAAACwAAAABAAQAAgAAAAAAAADdAAAABAAAANkAAAAMAAAAAAAAAAAAAADIAAAAAgAAAAIAAAAAAAAA1QAAAAQAAADRAAAADAAAAAAAAAAAAAAAwAAAAAIAAAACAAAAAAAAAM0AAAAEAAAAyQAAAAwAAAAAAAAAAAAAALgAAAACAAAAAQAAAAAAAADBAAAADAAAAMEAAAAMAAAAAAAAAAAAAACwAAAAAQAAAAMAAAAAAAAAvQAAAAwAAAC9AAAADAAAAAAAAAAAAAAAqAAAAAEAAQABAAAAAAAAALUAAAAUAAAAtQAAABQAAAAAAAAAAAAAAKQAAAABAAAAAAAAAAAAAAC9AAAADAAAAL0AAAAMAAAAAAAAAAAAAACcAAAAAQABAAEAAAAAAAAAtQAAABwAAAC5AAAADAAAAAAAAAAAAAAApAAAAAEAAAAGAAAAAAAAAK0AAAAsAAAAtQAAABQAAAAAAAAAAAAAALAAAAABAAIABgAAAAAAAACxAQAALAAAALkBAAAUAAAAAAAAAAAAAAC0AQAAAQACAAEAAAAAAAAAbQIAAAwAAABtAgAABAAAAAAAAAAAAAAAXAIAAAEAAAAAAAMAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAFAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAIAAAAAAAAAAwAAAAIAAAAFAAAABgAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAcAAAAAAAAAAwAAAAAAAAAFAAAAAgAAAAcAAAAAAAAACAAAAAAAAAABAAAAAAAAAAEAAAAIAAAAAQAAAAAAAAAJAAAACgAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAAAcAAAA8QAAABQAAAANAAAA9wAAAAAAAAAAAAAACwAAAAwAAAANAAAAAAAAABgAAAABAAQAAgAAAAAAAAB5AAAAFAAAAHkAAAAMAAAAAAAAAAAAAABkAAAAAgAAAAEAAAAAAAAAcQAAABQAAABxAAAAFAAAAAAAAAAAAAAAYAAAAAEAAAABAAAAAAAAAGkAAAAUAAAAaQAAABQAAAAAAAAAAAAAAFgAAAABAAAAAAAAAAAAAABxAAAAFAAAAHEAAAAUAAAAAAAAAAAAAABQAAAAAQABAAEAAAAAAAAAaQAAABwAAABtAAAADAAAAAAAAAAAAAAAWAAAAAEAAAABAAAAAAAAAGUAAAAcAAAAaQAAAAwAAAAAAAAAAAAAAFQAAAABAAAAAAAGAAAAAAAAAAAAAAAAAAsAAAANAAAADgAAAAAAAAACAAAAAAAAAAwAAAAOAAAADwAAABAAAAACAAAAAAAAAA8AAAALAAAAEQAAABIAAAAGAAAAAAAAAAAAAAACAAEAAAAAAAAAAQABAAAAAAAAAAAAAAAAAAAAEgAAABAAAAATAAAAFAAAAAMAAAAAAAAAEQAAAAsAAAATAAAAAAAAABUAAAAAAAAAAwAAAAAAAAAVAAAADgAAABQAAAAAAAAAFgAAAAAAAAAWAAAADQAAAAIAAAAAAAAAAQAAAAkAAAAEAAAAAAAAABcAAAAYAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABwAAAClAAAAFAAAAA0AAACnAAAAAAAAAAAAAAAZAAAAGgAAABsAAAAAAAAAEAAAAAEABAABAAAAAAAAAE0AAAAUAAAATQAAABQAAAAAAAAAAAAAADwAAAABAAAAAAAAAAAAAABJAAAADAAAAEkAAAAUAAAAAAAAAAAAAAA0AAAAAQABAAEAAAAAAAAAQQAAABwAAABFAAAADAAAAAAAAAAAAAAAMAAAAAEAAAADAAAAAAAAAEEAAAAcAAAARQAAAAwAAAAAAAAAAAAAACwAAAABAAEAAgAAAAAAAAAaAAAAGQAAABwAAAAdAAAABAAAAAAAAAAAAAAAAAAAAB0AAAAAAAAAHgAAAB8AAAADAAAAAAAAABwAAAAZAAAAHgAAAAAAAAAgAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAbAAAAGQAAAB8AAAAAAAAAIQAAAAAAAAAgAAAAIQAAAAEAAAAAAAAAFwAAAAAAAAAYAAAAAAAAAIgAAAAAAAIADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAwBAAACAAAAAAAAAAAAAAAMAQAAAgAAAAAAAAAAAAAADAEAAAIAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAABtcXQtY2MAADQuMC4wAAAA", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// From the benchmark's source: h on the first qubit, then each qubit controls x on the next.\nqubit[3] q;\nbit[3] c;\nh q[0];\ncx q[0], q[1];\ncx q[1], q[2];\nc = measure q;\n" + }, + { + "file": "jeff/upstream/ghz-star_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "how": "tableau", + "outcomes": [ + "000", + "111" + ], + "round_trip": true + }, + "base64": "AAAAAGMBAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAABpBQAAOgAAAGkFAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEADkDAAAXAgAAAAAAAAAAAAANAAAABAAAACkDAAAMAAAABQAAAL8BAAAAAAAAAAAAACwAAAABAAQAAgAAAAAAAADdAAAABAAAANkAAAAMAAAAAAAAAAAAAADIAAAAAgAAAAIAAAAAAAAA1QAAAAQAAADRAAAADAAAAAAAAAAAAAAAwAAAAAIAAAACAAAAAAAAAM0AAAAEAAAAyQAAAAwAAAAAAAAAAAAAALgAAAACAAAAAQAAAAAAAADBAAAADAAAAMEAAAAMAAAAAAAAAAAAAACwAAAAAQAAAAMAAAAAAAAAvQAAAAwAAAC9AAAADAAAAAAAAAAAAAAAqAAAAAEAAQABAAAAAAAAALUAAAAUAAAAtQAAABQAAAAAAAAAAAAAAKQAAAABAAAAAAAAAAAAAAC9AAAADAAAAL0AAAAMAAAAAAAAAAAAAACcAAAAAQABAAEAAAAAAAAAtQAAABwAAAC5AAAADAAAAAAAAAAAAAAApAAAAAEAAAAGAAAAAAAAAK0AAAAsAAAAtQAAABQAAAAAAAAAAAAAALAAAAABAAIABgAAAAAAAACNAQAALAAAAJUBAAAUAAAAAAAAAAAAAACQAQAAAQACAAEAAAAAAAAASQIAAAwAAABJAgAABAAAAAAAAAAAAAAAOAIAAAEAAAAAAAMAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAFAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAIAAAAAAAAAAwAAAAIAAAAFAAAABgAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAcAAAAAAAAAAwAAAAAAAAAFAAAAAgAAAAcAAAAAAAAACAAAAAAAAAABAAAAAAAAAAEAAAAIAAAAAgAAAAAAAAAJAAAACgAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAAAcAAAAzQAAABQAAAANAAAAzwAAAAAAAAAAAAAACwAAAAwAAAANAAAAAAAAABQAAAABAAQAAQAAAAAAAABhAAAAFAAAAGEAAAAUAAAAAAAAAAAAAABQAAAAAQAAAAEAAAAAAAAAWQAAABQAAABZAAAAFAAAAAAAAAAAAAAASAAAAAEAAAAAAAAAAAAAAGEAAAAUAAAAYQAAABQAAAAAAAAAAAAAAEAAAAABAAEAAQAAAAAAAABZAAAAHAAAAF0AAAAMAAAAAAAAAAAAAABIAAAAAQAAAAEAAAAAAAAAVQAAABwAAABZAAAADAAAAAAAAAAAAAAARAAAAAEAAAACAAAAAAAAAAwAAAANAAAADgAAAA8AAAACAAAAAAAAAA4AAAALAAAAEAAAABEAAAAGAAAAAAAAAAAAAAACAAEAAAAAAAAAAQABAAAAAAAAAAAAAAAAAAAAEQAAAA8AAAASAAAAEwAAAAMAAAAAAAAAEAAAAAsAAAASAAAAAAAAABQAAAAAAAAAAwAAAAAAAAAUAAAADQAAABMAAAAAAAAAFQAAAAAAAAAVAAAADQAAAAIAAAAAAAAAAQAAAAkAAAAEAAAAAAAAABYAAAAXAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABwAAAClAAAAFAAAAA0AAACnAAAAAAAAAAAAAAAYAAAAGQAAABoAAAAAAAAAEAAAAAEABAABAAAAAAAAAE0AAAAUAAAATQAAABQAAAAAAAAAAAAAADwAAAABAAAAAAAAAAAAAABJAAAADAAAAEkAAAAUAAAAAAAAAAAAAAA0AAAAAQABAAEAAAAAAAAAQQAAABwAAABFAAAADAAAAAAAAAAAAAAAMAAAAAEAAAADAAAAAAAAAEEAAAAcAAAARQAAAAwAAAAAAAAAAAAAACwAAAABAAEAAgAAAAAAAAAZAAAAGAAAABsAAAAcAAAABAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAHQAAAB4AAAADAAAAAAAAABsAAAAYAAAAHQAAAAAAAAAfAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAaAAAAGAAAAB4AAAAAAAAAIAAAAAAAAAAfAAAAIAAAAAEAAAAAAAAAFgAAAAAAAAAXAAAAAAAAAIQAAAAAAAIABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAABAEAAAIAAAAAAAAAAAAAAAQBAAACAAAAAAAAAAAAAAAEAQAAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// From the benchmark's source: h on the first qubit, which controls x on each other.\nqubit[3] q;\nbit[3] c;\nh q[0];\ncx q[0], q[1];\ncx q[0], q[2];\nc = measure q;\n" + }, + { + "file": "jeff/upstream/qft_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "how": "exact", + "round_trip": true + }, + "base64": "AAAAAEECAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAADhCAAAOgAAAOEIAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAOEEAADnAwAAAAAAAAAAAAANAAAABAAAANEEAAAMAAAABQAAAL8BAAAAAAAAAAAAACwAAAABAAQABAAAAAAAAADdAAAABAAAANkAAAAMAAAAAAAAAAAAAADIAAAAAgAAAAQAAAAAAAAA1QAAAAQAAADRAAAADAAAAAAAAAAAAAAAwAAAAAIAAAACAAAAAAAAAM0AAAAEAAAAyQAAAAwAAAAAAAAAAAAAALgAAAACAAAAAgAAAAAAAADFAAAABAAAAMEAAAAMAAAAAAAAAAAAAACwAAAAAgAAAAIAAAAAAAAAvQAAAAQAAAC5AAAADAAAAAAAAAAAAAAAqAAAAAIAAAACAAAAAAAAALUAAAAEAAAAsQAAAAwAAAAAAAAAAAAAAKAAAAACAAAAAQAAAAAAAACpAAAADAAAAKkAAAAMAAAAAAAAAAAAAACYAAAAAQAAAAMAAAAAAAAApQAAAAwAAAClAAAADAAAAAAAAAAAAAAAkAAAAAEAAQAGAAAAAAAAAJkAAABMAAAAqQAAADQAAAAAAAAAAAAAAKwAAAABAAIABgAAAAAAAAAJAwAANAAAABEDAAAcAAAAAAAAAAAAAAAQAwAAAQACAAEAAAAAAAAA8QMAAAwAAADxAwAABAAAAAAAAAAAAAAA4AMAAAEAAAAAAAAAAQAAAAAAAAAAAOA/AAAAAAAAAAAAAAAAAQAAABgtRFT7Ifk/AQAAAAAAAAAAAAMAAgAAAAAAAAAAAAAAAgAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAMAAQAAAAAAAAAAAAAABAAAAAAAAAAAAAMAAwAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAABgAAAAAAAAAFAAEAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAAMAAAAFAAAABAAAAAYAAAACAAAABAAAAAAAAAADAAAAAQAAAAAAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAPAAAAEUCAAA0AAAAFQAAAPcAAAAAAAAAAAAAAA4AAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAAAAAAGAAAAAEABAACAAAAAAAAAHkAAAAUAAAAeQAAAAwAAAAAAAAAAAAAAGQAAAACAAAAAQAAAAAAAABxAAAAFAAAAHEAAAAUAAAAAAAAAAAAAABgAAAAAQAAAAAAAAAAAAAAeQAAAAwAAAB5AAAADAAAAAAAAAAAAAAAWAAAAAEAAQABAAAAAAAAAHEAAAAcAAAAdQAAAAwAAAAAAAAAAAAAAGAAAAABAAAAAgAAAAAAAABxAAAAFAAAAHEAAAAMAAAAAAAAAAAAAABcAAAAAgAAAAYAAAAAAAAAZQAAAEQAAABxAAAALAAAAAAAAAAAAAAAdAAAAAEAAgAAAAYAAAAAAAAAAAAAAAAAEAAAAA4AAAAVAAAAAAAAAAIAAAAAAAAADwAAABUAAAAWAAAAFwAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAXAAAAAAAAABgAAAAAAAAAAwAAAAAAAAAWAAAAFQAAABgAAAAAAAAAGQAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAEQAAAA4AAAAaAAAAAAAAABMAAAAVAAAAEQAAABQAAAAZAAAAGgAAABUAAAASAAAAGwAAABwAAAAdAAAAHgAAAB8AAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAADQAAAAdAQAALAAAABEAAAAfAQAAAAAAAAAAAAAgAAAAIQAAACIAAAAjAAAAJAAAACUAAAAcAAAAAQAEAAIAAAAAAAAAjQAAABQAAACNAAAADAAAAAAAAAAAAAAAeAAAAAIAAAABAAAAAAAAAIUAAAAUAAAAhQAAABQAAAAAAAAAAAAAAHQAAAABAAAAAQAAAAAAAAB9AAAAFAAAAH0AAAAUAAAAAAAAAAAAAABsAAAAAQAAAAAAAAAAAAAAhQAAABwAAACJAAAAFAAAAAAAAAAAAAAAZAAAAAEAAQABAAAAAAAAAIEAAAAcAAAAhQAAAAwAAAAAAAAAAAAAAHAAAAABAAAAAQAAAAAAAAB9AAAAHAAAAIEAAAAMAAAAAAAAAAAAAABsAAAAAQAAAAQAAAAAAAAAfQAAABQAAAB9AAAADAAAAAAAAAAAAAAAaAAAAAIAAAAAAAYAAAAAAAAAAAAAAAAAIwAAACAAAAAmAAAAAAAAAAIAAAAAAAAAIgAAACYAAAAnAAAAKAAAAAIAAAAAAAAAJwAAACQAAAApAAAAKgAAAAYAAAAAAAAAAAAAAAIAAQAFAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAqAAAAKAAAACEAAAAAAAAAKwAAACwAAAADAAAAAAAAACkAAAAkAAAAKwAAAAAAAAAtAAAAAAAAAAMAAAAAAAAALQAAACYAAAAsAAAAAAAAAC4AAAAAAAAAAAAAAAQAAAAAAAAAAAAAACEAAAAlAAAALwAAAAAAAAAvAAAALgAAACMAAAAkAAAAJQAAAAAAAAAcAAAAEAAAABEAAAASAAAAEwAAABQAAAADAAAABQAAAAQAAAAIAAAAAgAAAAcAAAAwAAAAMQAAADIAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAACQAAADJAAAAHAAAAA0AAADPAAAAAAAAAAAAAAAzAAAANAAAADUAAAA2AAAAFAAAAAEABAACAAAAAAAAAGUAAAAUAAAAZQAAAAwAAAAAAAAAAAAAAFAAAAACAAAAAQAAAAAAAABdAAAAFAAAAF0AAAAUAAAAAAAAAAAAAABMAAAAAQAAAAAAAAAAAAAAWQAAAAwAAABZAAAAFAAAAAAAAAAAAAAARAAAAAEAAQABAAAAAAAAAFEAAAAcAAAAVQAAAAwAAAAAAAAAAAAAAEAAAAABAAAAAwAAAAAAAABRAAAAHAAAAFUAAAAMAAAAAAAAAAAAAAA8AAAAAQABAAAABgAAAAAAAAAAAAAAAAA1AAAAMwAAADcAAAAAAAAAAgAAAAAAAAA0AAAAMwAAADgAAAA5AAAABAAAAAAAAAAAAAAAAAAAADkAAAAAAAAAOgAAADsAAAADAAAAAAAAADgAAAAzAAAAOgAAAAAAAAA8AAAAAAAAAAcAAAAAAAAAAAAAAAAAAAA2AAAANwAAADsAAAAAAAAAPQAAAAAAAAA8AAAANQAAAD0AAAAAAAAAAQAAAAAAAAAwAAAAAAAAADIAAAAAAAAA+AAAAAAAAgDsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAOwBAAACAAAAAAAAAAAAAADsAQAAAgAAAAAAAAAAAAAA7AEAAAIAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAADAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// From the benchmark's source: the textbook QFT on three qubits, qubit i measured into bit 2 - i.\nqubit[3] q;\nbit[3] c;\nh q[2];\ncp(pi/2) q[1], q[2];\ncp(pi/4) q[0], q[2];\nh q[1];\ncp(pi/2) q[0], q[1];\nh q[0];\nc[2] = measure q[0];\nc[1] = measure q[1];\nc[0] = measure q[2];\n" + }, + { + "file": "jeff/upstream/grover_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "outcomes": [ + "11" + ], + "round_trip": true + }, + "base64": "AAAAANMCAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAAApCwAAOgAAACkLAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEANkGAAA3BAAAAAAAAAAAAAANAAAABAAAAMkGAAAMAAAABQAAAHcDAAAAAAAAAAAAAFgAAAABAAQAAgAAAAAAAAC5AQAABAAAALUBAAAMAAAAAAAAAAAAAACkAQAAAgAAAAIAAAAAAAAAsQEAAAQAAACtAQAADAAAAAAAAAAAAAAAnAEAAAIAAAACAAAAAAAAAKkBAAAEAAAApQEAAAwAAAAAAAAAAAAAAJQBAAACAAAAAQAAAAAAAACdAQAADAAAAJ0BAAAMAAAAAAAAAAAAAACMAQAAAQAAAAMAAAAAAAAAmQEAAAwAAACZAQAADAAAAAAAAAAAAAAAhAEAAAEAAQAGAAAAAAAAAI0BAAAkAAAAkQEAAAwAAAAAAAAAAAAAAIwBAAABAAIAAQAAAAAAAAAlAgAAFAAAACUCAAAUAAAAAAAAAAAAAAAUAgAAAQAAAAEAAAAAAAAAHQIAABQAAAAdAgAAFAAAAAAAAAAAAAAADAIAAAEAAAAAAAAAAAAAACUCAAAUAAAAJQIAABQAAAAAAAAAAAAAAAQCAAABAAEAAQAAAAAAAAAdAgAAHAAAACECAAAMAAAAAAAAAAAAAAAMAgAAAQAAAAEAAAAAAAAAGQIAABwAAAAdAgAADAAAAAAAAAAAAAAACAIAAAEAAAAGAAAAAAAAABECAAAkAAAAFQIAAAwAAAAAAAAAAAAAABACAAABAAIABgAAAAAAAAClAgAAJAAAAKkCAAAMAAAAAAAAAAAAAACkAgAAAQACAAEAAAAAAAAAPQMAABQAAAA9AwAAFAAAAAAAAAAAAAAALAMAAAEAAAABAAAAAAAAADUDAAAUAAAANQMAABQAAAAAAAAAAAAAACQDAAABAAAAAAAAAAAAAAA9AwAAFAAAAD0DAAAUAAAAAAAAAAAAAAAcAwAAAQABAAEAAAAAAAAANQMAABwAAAA5AwAADAAAAAAAAAAAAAAAJAMAAAEAAAABAAAAAAAAADEDAAAcAAAANQMAAAwAAAAAAAAAAAAAACADAAABAAAABgAAAAAAAAApAwAAJAAAAC0DAAAMAAAAAAAAAAAAAAAoAwAAAQACAAYAAAAAAAAAvQMAACQAAADBAwAADAAAAAAAAAAAAAAAvAMAAAEAAgAGAAAAAAAAAFEEAAAsAAAAWQQAABQAAAAAAAAAAAAAAFQEAAABAAIAAQAAAAAAAAANBQAADAAAAA0FAAAEAAAAAAAAAAAAAAD8BAAAAQAAAAAAAwACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwABAAAAAAAAAAAAAAABAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAUAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAgAAAAAAAAABAAAAAwAAAAUAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAGAAAABwAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAHAAAABgAAAAgAAAAJAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAkAAAAAAAAACgAAAAAAAAADAAAAAAAAAAgAAAAGAAAACgAAAAAAAAALAAAAAAAAAAsAAAAAAAAAAgAAAAAAAAAFAAAAAgAAAAwAAAANAAAAAgAAAAAAAAAMAAAAAQAAAA4AAAAPAAAABgAAAAAAAAAAAAAAAgABAAIAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAA8AAAANAAAAEAAAABEAAAADAAAAAAAAAA4AAAABAAAAEAAAAAAAAAASAAAAAAAAAAMAAAAAAAAAEgAAAAIAAAARAAAAAAAAABMAAAAAAAAAAgAAAAAAAAABAAAAEwAAABQAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAVAAAAFgAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAWAAAAFQAAABcAAAAYAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAABgAAAAAAAAAGQAAAAAAAAADAAAAAAAAABcAAAAVAAAAGQAAAAAAAAAaAAAAAAAAABoAAAAAAAAAAgAAAAAAAAABAAAAFAAAABsAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAcAAAAHQAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAdAAAAHAAAAB4AAAAfAAAABgAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAB8AAAAAAAAAIAAAAAAAAAADAAAAAAAAAB4AAAAcAAAAIAAAAAAAAAAhAAAAAAAAACEAAAAAAAAAAgAAAAAAAAAbAAAAAgAAACIAAAAjAAAAAgAAAAAAAAAiAAAAAQAAACQAAAAlAAAABgAAAAAAAAAAAAAAAgABAAIAAAAAAAEAAQAAAAAAAAAAAAAAAAAAACUAAAAjAAAAJgAAACcAAAADAAAAAAAAACQAAAABAAAAJgAAAAAAAAAoAAAAAAAAAAMAAAAAAAAAKAAAAAIAAAAnAAAAAAAAACkAAAAAAAAAAgAAAAAAAAABAAAAKQAAACoAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAArAAAALAAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAsAAAAKwAAAC0AAAAuAAAABgAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAC4AAAAAAAAALwAAAAAAAAADAAAAAAAAAC0AAAArAAAALwAAAAAAAAAwAAAAAAAAADAAAAAAAAAAAgAAAAAAAAABAAAAKgAAADEAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAyAAAAMwAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAzAAAAMgAAADQAAAA1AAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAADUAAAAAAAAANgAAAAAAAAADAAAAAAAAADQAAAAyAAAANgAAAAAAAAA3AAAAAAAAADcAAAAAAAAAAgAAAAAAAAABAAAAMQAAAAQAAAAAAAAAOAAAADkAAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAHAAAAKUAAAAUAAAADQAAAKcAAAAAAAAAAAAAADoAAAA7AAAAPAAAAAAAAAAQAAAAAQAEAAEAAAAAAAAATQAAABQAAABNAAAAFAAAAAAAAAAAAAAAPAAAAAEAAAAAAAAAAAAAAEkAAAAMAAAASQAAABQAAAAAAAAAAAAAADQAAAABAAEAAQAAAAAAAABBAAAAHAAAAEUAAAAMAAAAAAAAAAAAAAAwAAAAAQAAAAMAAAAAAAAAQQAAABwAAABFAAAADAAAAAAAAAAAAAAALAAAAAEAAQACAAAAAAAAADsAAAA6AAAAPQAAAD4AAAAEAAAAAAAAAAAAAAAAAAAAPgAAAAAAAAA/AAAAQAAAAAMAAAAAAAAAPQAAADoAAAA/AAAAAAAAAEEAAAAAAAAABwAAAAAAAAAAAAAAAAAAADwAAAA6AAAAQAAAAAAAAABCAAAAAAAAAEEAAABCAAAAAQAAAAAAAAA4AAAAAAAAADkAAAAAAAAADAEAAAAAAgAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAFAIAAAIAAAAAAAAAAAAAABQCAAACAAAAAAAAAAAAAAAUAgAAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAMAAQABAAAAAgAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAG1xdC1jYwAANC4wLjAAAAA=" + }, + { + "file": "jeff/upstream/teleportation.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": true, + "outcomes": [ + "000", + "010", + "100", + "110" + ], + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "notes": [ + "Q715" + ] + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + }, + "base64": "AAAAAKIBAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAABlBgAAOgAAAGUGAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAFUEAAD3AQAAAAAAAAAAAAANAAAABAAAAEUEAAAMAAAABQAAAE8DAAAAAAAAAAAAAFQAAAABAAQAAgAAAAAAAAClAQAABAAAAKEBAAAMAAAAAAAAAAAAAACQAQAAAgAAAAAAAAAAAAAAnQEAAAQAAACZAQAADAAAAAAAAAAAAAAAiAEAAAEAAQAAAAAAAAAAAJUBAAAEAAAAkQEAAAwAAAAAAAAAAAAAAIABAAABAAEAAAAAAAAAAACNAQAABAAAAIkBAAAMAAAAAAAAAAAAAAB4AQAAAQABAAIAAAAAAAAAhQEAAAQAAACBAQAADAAAAAAAAAAAAAAAcAEAAAIAAAADAAAAAAAAAH0BAAAMAAAAfQEAAAwAAAAAAAAAAAAAAGgBAAABAAEAAAAAAAAAAACFAQAADAAAAIUBAAAMAAAAAAAAAAAAAABkAQAAAQABAAAAAAAAAAAAjQEAAAwAAACNAQAADAAAAAAAAAAAAAAAbAEAAAEAAQAAAAAAAAAAAJUBAAAUAAAAlQEAABQAAAAAAAAAAAAAAHQBAAABAAEAAAAAAAAAAACdAQAAFAAAAJ0BAAAUAAAAAAAAAAAAAAB8AQAAAQABAAAAAAAAAAAApQEAAAwAAAClAQAADAAAAAAAAAAAAAAAhAEAAAEAAQAAAAAAAAAAAKEBAAAMAAAAoQEAABQAAAAAAAAAAAAAAIwBAAABAAEAAAAAAAAAAACdAQAADAAAAJ0BAAAUAAAAAAAAAAAAAACIAQAAAQABAAYAAAAAAAAAkQEAABQAAACRAQAADAAAAAAAAAAAAAAAjAEAAAEAAgAGAAAAAAAAABkCAAAUAAAAGQIAAAwAAAAAAAAAAAAAABQCAAABAAIAAAAAAAAAAAC1AgAADAAAALUCAAAMAAAAAAAAAAAAAACUAgAAAQABAAAAAAAAAAAAsQIAAAwAAACxAgAAFAAAAAAAAAAAAAAAnAIAAAEAAQADAAAAAAAAAK0CAAAcAAAAsQIAAAwAAAAAAAAAAAAAAJgCAAABAAEAAAAAAAAAAACtAgAADAAAAK0CAAAEAAAAAAAAAAAAAACYAgAAAQABAAAAAAAAAAAApQIAAAwAAAClAgAABAAAAAAAAAAAAAAAkAIAAAEAAQAAAAAAAAAAAJ0CAAAMAAAAnQIAAAQAAAAAAAAAAAAAAIgCAAABAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAADAAEAAAAAAAAAAAAAAAQAAAAAAAAABQABAAAAAAAAAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAAAAAACAAEACQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAcAAAAAAAAABgAAAAAAAAAAAAAAAgABAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAMAAAAHAAAACAAAAAkAAAAGAAAAAAAAAAAAAAACAAEAAAAAAAAAAQABAAAAAAAAAAAAAAAAAAAACQAAAAYAAAAKAAAACwAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAADQAAAA4AAAAEAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAPAAAAEAAAABAAAAAIAAAAEQAAAAAAAAAAAAAAAAAAAAUAAABHAAAAbAAAAAAABAAIAAAAAAAEAB0AAAAMAAAAIQAAAAwAAAAZAAAABwAAAAAAAAAAAAAAGQAAAAwAAABNAAAADAAAABUAAAAvAAAAAAAAAAAAAAASAAAAAAAAAAAAAAABAAQAEgAAAAAAAAATAAAAAAAAAAQAAAABAAQAAAAAAAAAAAAhAAAADAAAACEAAAAMAAAAAAAAAAAAAAAAAAAAAQABAAYAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAATAAAAAAAAABQAAAAAAAAAFAAAAAAAAAANAAAADAAAABEAAAAMAAAACQAAAAcAAAAAAAAAAAAAABUAAAAAAAAAAAAAAAEABAAVAAAAAAAAAA4AAAARAAAAFgAAAAAAAAAAAAAAAAAAAAUAAABHAAAAbAAAAAAABAAIAAAAAAAEAB0AAAAMAAAAIQAAAAwAAAAZAAAABwAAAAAAAAAAAAAAGQAAAAwAAABNAAAADAAAABUAAAAvAAAAAAAAAAAAAAAXAAAAAAAAAAAAAAABAAQAFwAAAAAAAAAYAAAAAAAAAAQAAAABAAQAAAAAAAAAAAAhAAAADAAAACEAAAAMAAAAAAAAAAAAAAAAAAAAAQABAAYAAAAAAAAAAAAAAAIAAQACAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAYAAAAAAAAABkAAAAAAAAAGQAAAAAAAAANAAAADAAAABEAAAAMAAAACQAAAAcAAAAAAAAAAAAAABoAAAAAAAAAAAAAAAEABAAaAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAWAAAAAAAAABsAAAAAAAAABAAAAAAAAAAAAAAAAAAAABsAAAAAAAAAHAAAAB0AAAAHAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAdAAAAAAAAAB4AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAA0AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAA8AAAAAAAAAAgAAAAAAAAAAAAAAAAAAABwAAAAAAAAAHgAAAAAAAAB8AAAAAAACAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAD0AAAAAgAAAAAAAAAAAAAA9AAAAAIAAAAAAAAAAAAAAPQAAAACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAADAAEAAQAAAAEAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==" + }, + { + "file": "jeff/upstream/iqft_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": true, + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "notes": [ + "Q715" + ] + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + }, + "base64": "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KQAAACoAAAAAAAAAAQAEACkAAAAqAAAAAAAAAAQAAAAAAAAAAAAAABsAAAAfAAAAKwAAAAAAAAArAAAAIgAAAB0AAAAeAAAAHwAAAAAAAAAGAAAAAAAAAAAAAAACAAEACQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAFgAAAAAAAAAsAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAC0AAAAuAAAABwAAAAAAAAAAAAAAAAAAABgAAAANAAAALgAAAAAAAAAvAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAtAAAAAAAAADAAAAAAAAAAMAAAAA8AAAAvAAAAEQAAABIAAAATAAAAAgAAAAAAAAAAAAAAAAAAAAcAAAAAAAAACQAAAAAAAADEAAAAAAACAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAACEAQAAAgAAAAAAAAAAAAAAhAEAAAIAAAAAAAAAAAAAAIQBAAACAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAADAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==" + }, + { + "file": "jeff/upstream/iqpe_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": true, + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "notes": [ + "Q715" + ] + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + }, + "base64": "AAAAAGgCAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAAB9CQAAOgAAAH0JAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAF0FAAAHBAAAAAAAAAAAAAANAAAABAAAAE0FAAAMAAAABQAAADcCAAAAAAAAAAAAADgAAAABAAQABAAAAAAAAAAZAQAABAAAABUBAAAMAAAAAAAAAAAAAAAEAQAAAgAAAAQAAAAAAAAAEQEAAAQAAAANAQAADAAAAAAAAAAAAAAA/AAAAAIAAAAFAAAAAAAAABUBAAAEAAAAEQEAAAwAAAAAAAAAAAAAAPQAAAABAAEAAgAAAAAAAAANAQAABAAAAAkBAAAMAAAAAAAAAAAAAAD4AAAAAgAAAAIAAAAAAAAABQEAAAQAAAABAQAADAAAAAAAAAAAAAAA8AAAAAIAAAACAAAAAAAAAP0AAAAEAAAA+QAAAAwAAAAAAAAAAAAAAOgAAAACAAAAAgAAAAAAAAD1AAAABAAAAPEAAAAMAAAAAAAAAAAAAADgAAAAAgAAAAAAAAAAAAAA7QAAAAQAAADpAAAADAAAAAAAAAAAAAAA2AAAAAEAAQAAAAAAAAAAAOUAAAAEAAAA4QAAAAwAAAAAAAAAAAAAANAAAAABAAEAAwAAAAAAAADdAAAADAAAAN0AAAAMAAAAAAAAAAAAAADIAAAAAQABAAAAAAAAAAAA5QAAAAwAAADlAAAADAAAAAAAAAAAAAAAxAAAAAEAAQAGAAAAAAAAANkAAABkAAAA7QAAAEwAAAAAAAAAAAAAAPgAAAABAAIAAAAAAAAAAAA5BAAADAAAADkEAAAEAAAAAAAAAAAAAAAkBAAAAQABAAAAAAAAAAAAMQQAAAwAAAAxBAAABAAAAAAAAAAAAAAAHAQAAAEAAQAAAAAAAQAAAAAAAAAAAOA/AAAAAAAAAAAAAAAAAQAAABgtRFT7Ifm/AQAAAAAAAAABAAAAAAAAAAEAAAAdAAAA0iEzf3zZ8j/SITN/fNkCQNIhM3982RJAAgAAAAAAAAAAAAMAAgAAAAAAAAAAAAAAAwAAAAAAAAAAAAMAAQAAAAAAAAAAAAAABAAAAAAAAAAAAAMAAwAAAAAAAAAAAAAABQAAAAAAAAAAAAMAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAFAAEAAAAAAAAAAAAAAAAABQAAAAAAAAAJAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAoAAAAAAAAABgAAAAUAAAAEAAAABwAAAAoAAAADAAAAAgAAAAQAAAAJAAAAAAAAAAYAAAABAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAAAAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAABUAAAAGQMAAEwAAAAZAAAAlwEAAAAAAAAAAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAcAAAAHQAAACgAAAABAAQAAgAAAAAAAADJAAAAFAAAAMkAAAAMAAAAAAAAAAAAAAC0AAAAAgAAAAUAAAAAAAAAxQAAABQAAADFAAAADAAAAAAAAAAAAAAAsAAAAAEAAQAAAAAAAAAAAM0AAAAMAAAAzQAAAAwAAAAAAAAAAAAAAKwAAAABAAEAAAAAAAAAAADVAAAAHAAAANkAAAAUAAAAAAAAAAAAAAC0AAAAAQABAAIAAAAAAAAA1QAAABQAAADVAAAADAAAAAAAAAAAAAAAwAAAAAIAAAAGAAAAAAAAAMkAAABEAAAA1QAAACwAAAAAAAAAAAAAANgAAAABAAIAAAAAAAAAAAA9AgAADAAAAD0CAAAMAAAAAAAAAAAAAAAcAgAAAQABAAAAAAAAAAAAOQIAAAwAAAA5AgAAFAAAAAAAAAAAAAAAJAIAAAEAAQADAAAAAAAAADUCAAAcAAAAOQIAAAwAAAAAAAAAAAAAACACAAABAAEAAAAAAAAAAAA1AgAADAAAADUCAAAMAAAAAAAAAAAAAAAgAgAAAQABAAAABgAAAAAAAAAAAAAAAAAXAAAAFAAAAB4AAAAAAAAAAwAAAAAAAAAAAAAAAAAAABgAAAAeAAAAHwAAAAAAAAAGAAAAAAAAAAAAAAACAAEACQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAFQAAAAAAAAAgAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAFAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAWAAAAIAAAAB8AAAAAAAAAIQAAACIAAAAAAAYAAAAAAAAAAAAAAAAAFAAAABkAAAAjAAAAAAAAABwAAAAUAAAAGQAAAB0AAAAiAAAAIwAAABoAAAAbAAAAJAAAACUAAAAmAAAAJwAAACgAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAADQAAAA5AQAALAAAABEAAACnAAAAAAAAAAAAAAApAAAAKgAAACsAAAAsAAAALQAAAC4AAAAQAAAAAQAEAAIAAAAAAAAAUQAAABQAAABRAAAADAAAAAAAAAAAAAAAPAAAAAIAAAADAAAAAAAAAE0AAAAUAAAATQAAAAwAAAAAAAAAAAAAADgAAAABAAEABgAAAAAAAABBAAAAHAAAAEUAAAAUAAAAAAAAAAAAAABAAAAAAQACAAQAAAAAAAAA1QAAABQAAADVAAAADAAAAAAAAAAAAAAAwAAAAAIAAAAAAAYAAAAAAAAAAAAAAAAALAAAACkAAAAvAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAtAAAALwAAADAAAAAAAAAAMAAAACsAAAAqAAAAAAAAADEAAAAyAAAAAAAAAAAAAAAFAAAARwAAAGwAAAAAAAQACAAAAAAABAAdAAAAFAAAACEAAAAUAAAAGQAAAAcAAAAAAAAAAAAAABkAAAAUAAAATQAAABQAAAAVAAAALwAAAAAAAAAAAAAAMwAAADQAAAAAAAAAAQAEADMAAAA0AAAANQAAADYAAAAEAAAAAQAEAAAAAAAAAAAAIQAAABQAAAAhAAAADAAAAAAAAAAAAAAAAAAAAAEAAQAGAAAAAAAAAAAAAAACAAEABQAAAAAAAAABAAAAAAAAAAAAAAAAAAAANQAAADYAAAA3AAAAAAAAADcAAAA2AAAADQAAABQAAAARAAAAFAAAAAkAAAAHAAAAAAAAAAAAAAA4AAAAOQAAAAAAAAABAAQAOAAAADkAAAAAAAAABAAAAAAAAAAAAAAAKgAAAC4AAAA6AAAAAAAAADoAAAAxAAAALAAAAC0AAAAuAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAlAAAAAAAAADsAAAAAAAAABAAAAAAAAAAAAAAAAAAAADsAAAAAAAAAPAAAAD0AAAAHAAAAAAAAAAAAAAAAAAAAJwAAABQAAAA9AAAAAAAAAD4AAAAAAAAABQAAAAAAAAAAAAAAAAAAADwAAAAAAAAAPwAAAAAAAAA/AAAAIQAAABcAAAAYAAAAGQAAAD4AAAAbAAAAHAAAAB0AAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAAAAQAAAAACAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAD8AQAAAgAAAAAAAAAAAAAA/AEAAAIAAAAAAAAAAAAAAPwBAAACAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAUAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAUAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAADAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAAAMAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==" + }, + { + "file": "jeff/upstream/qpe_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + }, + "base64": "AAAAAAwDAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAAANDAAAOgAAAA0MAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEAN0GAAAXBQAAAAAAAAAAAAANAAAABAAAAM0GAAAMAAAABQAAAIcCAAAAAAAAAAAAAEAAAAABAAQABAAAAAAAAABBAQAABAAAAD0BAAAMAAAAAAAAAAAAAAAsAQAAAgAAAAQAAAAAAAAAOQEAAAQAAAA1AQAADAAAAAAAAAAAAAAAJAEAAAIAAAAFAAAAAAAAADkBAAAEAAAANQEAAAwAAAAAAAAAAAAAABwBAAABAAEAAgAAAAAAAAAxAQAABAAAAC0BAAAMAAAAAAAAAAAAAAAcAQAAAgAAAAIAAAAAAAAAKQEAAAQAAAAlAQAADAAAAAAAAAAAAAAAFAEAAAIAAAACAAAAAAAAACEBAAAEAAAAHQEAAAwAAAAAAAAAAAAAAAwBAAACAAAAAQAAAAAAAAAVAQAADAAAABUBAAAMAAAAAAAAAAAAAAAEAQAAAQAAAAAAAAAAAAAAEQEAAAQAAAANAQAADAAAAAAAAAAAAAAA/AAAAAEAAQADAAAAAAAAAAkBAAAMAAAACQEAAAwAAAAAAAAAAAAAAPQAAAABAAEABgAAAAAAAAD9AAAAJAAAAAEBAAAMAAAAAAAAAAAAAAD8AAAAAQACAAAAAAAAAAAApQEAAAwAAAClAQAADAAAAAAAAAAAAAAAhAEAAAEAAQAGAAAAAAAAAJkBAAA8AAAApQEAACQAAAAAAAAAAAAAAKQBAAABAAIABgAAAAAAAACRAgAARAAAAJ0CAAAsAAAAAAAAAAAAAACgAgAAAQACAAYAAAAAAAAA1QQAACwAAADdBAAAFAAAAAAAAAAAAAAA2AQAAAEAAgAAAAAAAAAAAJUFAAAMAAAAlQUAAAQAAAAAAAAAAAAAAIAFAAABAAEAAQAAAAAAAACJBQAADAAAAIkFAAAEAAAAAAAAAAAAAAB4BQAAAQAAAAAAAAABAAAAAAAAAAAA4D8AAAAAAAAAAAAAAAABAAAAGC1EVPsh+b8BAAAAAAAAAAEAAAAAAAAAAQAAABUAAADSITN/fNnyP9IhM3982QJAAgAAAAAAAAAAAAMAAQAAAAAAAAAAAAAAAwAAAAAAAAAAAAMAAgAAAAAAAAAAAAAABAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAFAAEAAAAAAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAUAAAAEAAAAAwAAAAYAAAAJAAAAAAAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAAAUAAAAhQAAAAwAAAAJAAAAfwAAAAAAAAAAAAAACgAAAAsAAAAMAAAAAQAEAAEAAAAAAAAAOQAAABQAAAA5AAAAFAAAAAAAAAAAAAAAKAAAAAEAAAAAAAAAAAAAAEEAAAAMAAAAQQAAAAwAAAAAAAAAAAAAACAAAAABAAEAAQAAAAAAAAA5AAAAHAAAAD0AAAAMAAAAAAAAAAAAAAAoAAAAAQAAAAIAAAAAAAAACwAAAAoAAAAMAAAADQAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAANAAAAAAAAAA4AAAAAAAAAAwAAAAAAAAAMAAAACgAAAA4AAAAAAAAADwAAAAAAAAAPAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAHAAAAAAAAABAAAAAAAAAABQAAAAQAAAADAAAACQAAABAAAAACAAAAAwAAAAAAAAARAAAAEgAAABMAAAAUAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAACwAAADZAAAAJAAAABEAAADPAAAAAAAAAAAAAAAVAAAAFgAAABcAAAAYAAAAGQAAAAAAAAAUAAAAAQAEAAUAAAAAAAAAZQAAABQAAABlAAAADAAAAAAAAAAAAAAAUAAAAAEAAQACAAAAAAAAAGEAAAAUAAAAYQAAAAwAAAAAAAAAAAAAAEwAAAACAAAAAQAAAAAAAABZAAAAFAAAAFkAAAAUAAAAAAAAAAAAAABIAAAAAQAAAAAAAAAAAAAAYQAAABwAAABlAAAAFAAAAAAAAAAAAAAAQAAAAAEAAQABAAAAAAAAAF0AAAAcAAAAYQAAAAwAAAAAAAAAAAAAAEwAAAABAAAAAwAAAAAAAAAAAAAAAAAAABgAAAAVAAAA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EgAAAAAAAAABAAAAAAAAAEYAAAAAAAAARwAAAAAAAABEAQAAAAACAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAIQCAAACAAAAAAAAAAAAAACEAgAAAgAAAAAAAAAAAAAAhAIAAAIAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABQABAAEAAAACAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAAEAAQACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAEAAQAAAAIAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAQABAAAAAgAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAwABAAEAAAACAAAAAAAAAG1xdC1jYwAANC4wLjAAAAA=" + }, + { + "file": "jeff/upstream/multiplexer_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + }, + "base64": "AAAAAAsCAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAAZAAAAJwAAAA0AAAAOAAAAAAAAAAAAAAAJCAAAOgAAAAkIAAAyAAAAAQAAACoAAABtYWluAAAAAAQAAAABAAMAAAAAAAAAAAAIAAAAAAAEALkEAAA3AwAAAAAAAAAAAAANAAAABAAAAKkEAAAMAAAABQAAAIcCAAAAAAAAAAAAAEAAAAABAAQABAAAAAAAAABBAQAABAAAAD0BAAAMAAAAAAAAAAAAAAAsAQAAAgAAAAQAAAAAAAAAOQEAAAQAAAA1AQAADAAAAAAAAAAAAAAAJAEAAAIAAAACAAAAAAAAADEBAAAEAAAALQEAAAwAAAAAAAAAAAAAABwBAAACAAAAAgAAAAAAAAApAQAABAAAACUBAAAMAAAAAAAAAAAAAAAUAQAAAgAAAAIAAAAAAAAAIQEAAAQAAAAdAQAADAAAAAAAAAAAAAAADAEAAAIAAAABAAAAAAAAABUBAAAMAAAAFQEAAAwAAAAAAAAAAAAAAAQBAAABAAAAAAAAAAAAAAARAQAABAAAAA0BAAAMAAAAAAAAAAAAAAD8AAAAAQABAAIAAAAAAAAACQEAAAQAAAAFAQAADAAAAAAAAAAAAAAA9AAAAAIAAAADAAAAAAAAAAEBAAAMAAAAAQEAAAwAAAAAAAAAAAAAAOwAAAABAAEABgAAAAAAAAD1AAAAJAAAAPkAAAAMAAAAAAAAAAAAAAD0AAAAAQACAAYAAAAAAAAAiQEAAEQAAACVAQAALAAAAAAAAAAAAAAAmAEAAAEAAgAAAAAAAAAAAJECAAAMAAAAkQIAABQAAAAAAAAAAAAAAHwCAAABAAEAAwAAAAAAAACNAgAAHAAAAJECAAAMAAAAAAAAAAAAAAB4AgAAAQABAAYAAAAAAAAAhQIAADQAAACNAgAAHAAAAAAAAAAAAAAAjAIAAAEAAgAAAAAAAAAAAHEDAAAMAAAAcQMAAAQAAAAAAAAAAAAAAFwDAAABAAEAAQAAAAAAAABlAwAADAAAAGUDAAAEAAAAAAAAAAAAAABUAwAAAQAAAAAAAAABAAAAAAAAAAAA4D8AAAAAAAAAAAAAAAABAAAAGC1EVPsh+T8BAAAAAAAAAAAAAwACAAAAAAAAAAAAAAACAAAAAAAAAAAAAwABAAAAAAAAAAAAAAADAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAwADAAAAAAAAAAAAAAAHAAAAAAAAAAUAAQAAAAAAAAAAAAAAAAAHAAAAAAAAAAgAAAAAAAAABAAAAAIAAAADAAAABQAAAAkAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAKAAAACwAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAALAAAACgAAAAwAAAANAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA0AAAAAAAAADgAAAAAAAAADAAAAAAAAAAwAAAAKAAAADgAAAAAAAAAPAAAAAAAAAA8AAAAAAAAABAAAAAIAAAADAAAAAQAAAAkAAAAGAAAAAwAAAAAAAAAQAAAAEQAAABIAAAATAAAAFAAAAAAAAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAANAAAANkAAAAsAAAAEQAAAM8AAAAAAAAAAAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABQAAAABAAQAAgAAAAAAAABlAAAAFAAAAGUAAAAMAAAAAAAAAAAAAABQAAAAAgAAAAEAAAAAAAAAXQAAABQAAABdAAAAFAAAAAAAAAAAAAAATAAAAAEAAAAAAAAAAAAAAGUAAAAcAAAAaQAAABQAAAAAAAAAAAAAAEQAAAABAAEAAQAAAAAAAABhAAAAHAAAAGUAAAAMAAAAAAAAAAAAAABQAAAAAQAAAAQAAAAAAAAAYQAAABQAAABhAAAADAAAAAAAAAAAAAAATAAAAAIAAAAAAAYAAAAAAAAAAAAAAAAAGQAAABUAAAAbAAAAAAAAAAIAAAAAAAAAFwAAABsAAAAcAAAAHQAAAAYAAAAAAAAAAAAAAAIAAQAHAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAYAAAAHQAAABYAAAAAAAAAHgAAAB8AAAADAAAAAAAAABwAAAAbAAAAHwAAAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAWAAAAGgAAACEAAAAAAAAAIQAAACAAAAAeAAAAGQAAABoAAAAAAAAABAAAAAAAAAAAAAAAAAAAABIAAAAAAAAAIgAAACMAAAAHAAAAAAAAAAAAAAAAAAAACAAAAAQAAAAjAAAAAAAAACQAAAAAAAAABAAAAAIAAAADAAAAEQAAAAMAAAAkAAAAJQAAACYAAAAnAAAAAAAAAAEAAAAAAAAABAAAAAAABAAAAAAAAAAAAA0AAAAkAAAAyQAAABwAAAANAAAAzwAAAAAAAAAAAAAAKAAAACkAAAAqAAAAKwAAABQAAAABAAQAAgAAAAAAAABlAAAAFAAAAGUAAAAMAAAAAAAAAAAAAABQAAAAAgAAAAEAAAAAAAAAXQAAABQAAABdAAAAFAAAAAAAAAAAAAAATAAAAAEAAAAAAAAAAAAAAFkAAAAMAAAAWQAAABQAAAAAAAAAAAAAAEQAAAABAAEAAQAAAAAAAABRAAAAHAAAAFUAAAAMAAAAAAAAAAAAAABAAAAAAQAAAAMAAAAAAAAAUQAAABwAAABVAAAADAAAAAAAAAAAAAAAPAAAAAEAAQAAAAUAAAAAAAAAAAAAAAAAKAAAACoAAAAsAAAAAAAAAAIAAAAAAAAAKQAAACgAAAAtAAAALgAAAAQAAAAAAAAAAAAAAAAAAAAuAAAAAAAAAC8AAAAwAAAAAwAAAAAAAAAtAAAAKAAAAC8AAAAAAAAAMQAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAKwAAACwAAAAwAAAAAAAAADIAAAAAAAAAMQAAACoAAAAyAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAEAAAAAAAAAJQAAAAAAAAAnAAAAAAAAAMwAAAAAAAIAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAAlAEAAAIAAAAAAAAAAAAAAJQBAAACAAAAAAAAAAAAAACUAQAAAgAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAwABAAEAAAADAAAAAAAAAAEAAQACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAADAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAEAAQACAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAA==" + }, + { + "file": "jeff/upstream/qft-adder-classical_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + }, + "base64": "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AAAAAGsAAAAAAAAAbAAAAG0AAAADAAAAAAAAAGoAAABnAAAAbAAAAAAAAABuAAAAAAAAAAcAAAAAAAAAAAAAAAAAAABpAAAAZwAAAG0AAAAAAAAAbwAAAAAAAABuAAAAbwAAAAEAAAAAAAAAZQAAAAAAAABmAAAAAAAAAMABAAAAAAIAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAfAMAAAIAAAAAAAAAAAAAAHwDAAACAAAAAAAAAAAAAAB8AwAAAgAAAAAAAAAAAAAAAgAAAAEAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAFAAEAAQAAAAMAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAMAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAFAAEAAQAAAAMAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAFAAEAAQAAAAMAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAwAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAG1xdC1jYwAAAAAAAAIAAAADAAEAAQAAAAMAAAAAAAAAAQAAADIAAAA0LjAuMAAAAA==" + }, + { + "file": "jeff/upstream/qft-adder-quantum_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + }, + "base64": "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AAAAAKMAAACcAAAApAAAAAAAAAABAAAAAAAAAJcAAAAAAAAAAQAAAAAAAACMAAAAAAAAAJkAAAAAAAAAbXF0LWNjAAA0LjAuMAAAAAEAAABXCgAAlAIAAAAAAgAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAkBQAAAgAAAAAAAAAAAAAAJAUAAAIAAAAAAAAAAAAAACQFAAACAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAAGAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAYAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAYAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAADAAEAAQAAAAYAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAYAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAwABAAEAAAAGAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAABgAAAAAAAAA=" + }, + { + "file": "jeff/upstream/controlled-multiplication-modulo-n_3.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + }, + "base64": "AgAAAAAEAADjAwAA7gYAAAAAAAACAAUAAAAAAAAAAAADAAAAAAAAABkAAAAnAAAADQAAAA4AAAAAAAAAAAAAAFI3AAACAAAAYjcAAAIAAAABAAAAKgAAAG1haW4AAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAAgAAAAIAAAAAAAAAAAAAAA0AAAAEAAAACh8AAAEAAAAFAAAA3wQAAAAAAAAAAAAAfAAAAAEABAACAAAAAAAAAG0CAAAEAAAAaQIAAAwAAAAAAAAAAAAAAFgCAAACAAAAAgAAAAAAAABlAgAABAAAAGECAAAMAAAAAAAAAAAAAABQAgAAAgAAAAQAAAAAAAAAXQIAAAQAAABZAgAADAAAAAAAAAAAAAAASAIAAAIAAAAEAAAAAAAAAFUCAAAEAAAAUQIAAAwAAAAAAAAAAAAAAEACAAACAAAAAgAAAAAAAABNAgAABAAAAEkCAAAMAAAAAAAAAAAAAAA4AgAAAgAAAAUAAAAAAAAAhQIAAAQAAACBAgAADAAAAAAAAAAAAAAAMAIAAAEAAQACAAAAAAAAAH0CAAAEAAAAeQIAAAwAAAAAAAAAAAAAAGgCAAACAAAABAAAAAAAAAB1AgAABAAAAHECAAAMAAAAAAAAAAAAAABgAgAAAgAAAAQAAAAAAAAAbQIAAAQAAABpAgAADAAAAAAAAAAAAAAAWAIAAAIAAAACAAAAAAAAAGUCAAAEAAAAYQIAAAwAAAAAAAAAAAAAAFACAAACAAAAAgAAAAAAAABdAgAABAAAAFkCAAAMAAAAAAAAAAAAAABIAgAAAgAAAAIAAAAAAAAAVQIAAAQAAABRAgAADAAAAAAAAAAAAAAAQAIAAAIAAAAAAAAAAAAAAE0CAAAEAAAASQIAAAwAAAAAAAAAAAAAADgCAAABAAEAAQAAAAAAAABBAgAADAAAAEECAAAMAAAAAAAAAAAAAAAwAgAAAQAAAAEAAAAAAAAAOQIAAAwAAAA5AgAADAAAAAAAAAAAAAAAKAIAAAEAAAAAAAAAAAAAADUCAAAEAAAAMQIAAAwAAAAAAAAAAAAAACACAAABAAEAAgAAAAAAAAAtAgAABAAAACkCAAAMAAAAAAAAAAAAAAAYAgAAAgAAAAMAAAAAAAAAJQIAAAwAAAAlAgAADAAAAAAAAAAAAAAAEAIAAAEAAQAAAAAAAAAAAC0CAAAMAAAALQIAAAwAAAAAAAAAAAAAAAwCAAABAAEABgAAAAAAAAAhAgAAJAAAACUCAAAMAAAAAAAAAAAAAAAgAgAAAQACAAYAAAAAAAAAtQIAAFQAAADFAgAAPAAAAAAAAAAAAAAAzAIAAAEAAgAGAAAAAAAAAC0FAACUAAAATQUAAHwAAAAAAAAAAAAAAGQFAAABAAIABgAAAAAAAACCFQAAAQAAAKoVAAABAAAAAAAAAAAAAADKFQAAAQAAAAYAAAAAAAAAShoAAAEAAABqGgAAAQAAAAAAAAAAAAAAehoAAAEAAAAGAAAAAAAAAPobAAABAAAAGhwAAAEAAAAAAAAAAAAAADIcAAABAAAAAAAAAAAAAAAaHgAAAQAAACoeAAABAAAAAAAAAAAAAAACHgAAAQAAAAMAAAAAAAAAUh4AAAEAAABqHgAAAQAAAAAAAAAAAAAAOh4AAAEAAAAAAAAAAAAAAJIeAAABAAAAiQ0AAAQAAAAAAAAAAAAAAHoeAAABAAAAAAAAAAAAAAC6HgAAAQAAAHUNAAAEAAAAAAAAAAAAAACiHgAAAQAAAAEAAAAAAAAA2h4AAAEAAABhDQAABAAAAAAAAAAAAAAAyh4AAAEAAAABAAAAAAAAAPoeAAABAAAATQ0AAAQAAAAAAAAAAAAAAOoeAAABAAAAAAADAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAcAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAYLURU+yH5vwIAAAAAAAAAAAAAAAEAAAAAAAAAAADwvwMAAAAAAAAAAAADAAwAAAAAAAAAAAAAAAQAAAAAAAAAAQAAAAAAAAABAAAAhQAAABgtRFT7IQlA0iEzf3zZEkDSITN/fNkCQNIhM3982fI/GC1EVPshCUAYLURU+yH5PxgtRFT7Iek/GC1EVPsh2T8AAAAAAAAAABgtRFT7IQlAGC1EVPsh+T8YLURU+yHpPxgtRFT7IQlAGC1EVPsh+T9eOFUpemoPQF44VSl6av8/BQAAAAAAAAAAAAMABAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAQAAAAAAAAAAAOA/BwAAAAAAAAAAAAAAAQAAABgtRFT7Ifk/CAAAAAAAAAAAAAMAAQAAAAAAAAAAAAAACQAAAAAAAAAAAAMAAwAAAAAAAAAAAAAACgAAAAAAAAAAAAMAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAADQAAAAAAAAAAAAAAAAAAAAYAAAAAAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAAAAAAAAAMACAAAAAAAAAAAAAAAEAAAAAAAAAAFAAEAAAAAAAAAAAAAAAAAEAAAAAAAAAARAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAMAAAAAAAAABIAAAAAAAAACwAAAAoAAAAJAAAADQAAABMAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAAAUAAAAFQAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAAAVAAAAFAAAABYAAAAXAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAABcAAAAAAAAAGAAAAAAAAAADAAAAAAAAABYAAAAUAAAAGAAAAAAAAAAZAAAAAAAAABkAAAAAAAAACwAAAAYAAAAJAAAADgAAAAoAAAAAAAAABwAAAAsAAAAJAAAACAAAABoAAAAbAAAAHAAAAB0AAAAeAAAAHwAAACAAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAAEQAAABFAgAAPAAAABUAAAD3AAAAAAAAAAAAAAAhAAAAIgAAACMAAAAkAAAAJQAAACYAAAAnAAAAKAAAABgAAAABAAQAAgAAAAAAAAB5AAAAFAAAAHkAAAAMAAAAAAAAAAAAAABkAAAAAgAAAAEAAAAAAAAAcQAAABQAAABxAAAAFAAAAAAAAAAAAAAAYAAAAAEAAAAAAAAAAAAAAHkAAAAMAAAAeQAAAAwAAAAAAAAAAAAAAFgAAAABAAEAAQAAAAAAAABxAAAAHAAAAHUAAAAMAAAAAAAAAAAAAABgAAAA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zA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAzA0AAAIAAAAAAAAAAAAAAMwNAAACAAAAAAAAAAAAAADMDQAAAgAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAABAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAUAAQABAAAAEAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAABAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAABAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABQABAAEAAAAQAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABQABAAEAAAAQAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAABAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAFAAEAAQAAABAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAUAAQABAAAAEAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAUAAQABAAAAEAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABQABAAEAAAAQAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABQABAAEAAAAQAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAUAAQABAAAAEAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAMAAQABAAAACAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAMAAQABAAAACAAAAAAAAAABAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQABAAQAAAAAAAAAAAAAAAMAAQABAAAACAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAACAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAgAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAwABAAEAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAwABAAEAAAAIAAAAAAAAAAEAAAA6AAAAbXF0LWNjAAABAAAAMgAAADQuMC4wAAAA" + }, + { + "file": "jeff/upstream/repeat-until-success_3.jeff", + "format": "jeff", + "expect": { + "refused": "Q714", + "at": "operation 10" + }, + "base64": "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CgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAFAAAABwAAAAVAAAAFgAAAB0AAAAAAAAAFwAAABgAAAAZAAAAEwAAAB0AAAAAAAAAHgAAAB8AAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAHAAAAIkAAAAUAAAADQAAAH8AAAAAAAAAAAAAACAAAAAhAAAAIgAAAAAAAAAMAAAAAQAEAAEAAAAAAAAAOQAAABQAAAA5AAAAFAAAAAAAAAAAAAAAKAAAAAEAAAAAAAAAAAAAAEEAAAAUAAAAQQAAABQAAAAAAAAAAAAAACAAAAABAAEAAQAAAAAAAAA5AAAAHAAAAD0AAAAMAAAAAAAAAAAAAAAoAAAAAQAAAAIAAAAAAAAAIQAAACAAAAAjAAAAJAAAAAYAAAAAAAAAAAAAAAIAAQAAAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAkAAAAIgAAACUAAAAmAAAAAwAAAAAAAAAjAAAAIAAAACUAAAAAAAAAJwAAAAAAAAAnAAAAJgAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAfAAAAAAAAACgAAAAAAAAAFwAAABgAAAAZAAAAHgAAACgAAAAAAAAAKQAAACoAAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAHAAAAIkAAAAUAAAADQAAAH8AAAAAAAAAAAAAACsAAAAsAAAALQAAAAAAAAAMAAAAAQAEAAEAAAAAAAAAOQAAABQAAAA5AAAAFAAAAAAAAAAAAAAAKAAAAAEAAAAAAAAAAAAAAEEAAAAUAAAAQQAAABQAAAAAAAAAAAAAACAAAAABAAEAAQAAAAAAAAA5AAAAHAAAAD0AAAAMAAAAAAAAAAAAAAAoAAAAAQAAAAIAAAAAAAAALAAAACsAAAAuAAAALwAAAAYAAAAAAAAAAAAAAAIAAQAAAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAvAAAALQAAADAAAAAxAAAAAwAAAAAAAAAuAAAAKwAAADAAAAAAAAAAMgAAAAAAAAAyAAAAMQAAAAYAAAAAAAAAAAAAAAIAAQAKAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAqAAAAHAAAABoAAAAbAAAAMwAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAMwAAAAAAAAA0AAAANQAAADUAAAApAAAANAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAANAAAATAAAAFEAAABMAAAAGQAAAC8AAAAAAAAAAAAAADYAAAA3AAAAOAAAADkAAAA6AAAAOwAAADwAAAA9AAAAPgAAAAAAAAAEAAAAAQAEAAAAAAAAAAAAIQAAAAwAAAAhAAAADAAAAAAAAAAAAAAAAAAAAAEAAQAGAAAAAAAAAAAAAAACAAEAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAANwAAAAAAAAA/AAAAAAAAADYAAAA/AAAAOAAAADkAAAA6AAAAOwAAADwAAAA9AAAAPgAAAAAAAAACAAAAAAAAAAoAAAAGAAAAQAAAAEEAAAAGAAAAAAAAAAAAAAACAAEAAwAAAAAAAAEBAAAAAAAAAAAAAAAAAAAAQQAAAAAAAABCAAAAAAAAAAMAAAAAAAAAQAAAAAYAAABCAAAAAAAAAEMAAAAAAAAABgAAAAUAAAAEAAAAQwAAAEQAAAAAAAAAAQAAAAAAAAAEAAAAAAAEAAAAAAAAAAAADQAAABQAAACFAAAADAAAAAkAAAB/AAAAAAAAAAAAAABFAAAARgAAAAwAAAABAAQAAQAAAAAAAAA5AAAAFAAAADkAAAAUAAAAAAAAAAAAAAAoAAAAAQAAAAAAAAAAAAAAQQAAAAwAAABBAAAADAAAAAAAAAAAAAAAIAAAAAEAAQABAAAAAAAAADkAAAAcAAAAPQAAAAwAAAAAAAAAAAAAACgAAAABAAAAAgAAAAAAAABGAAAARQAAAEcAAABIAAAABgAAAAAAAAAAAAAAAgABAAkAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAEgAAAAAAAAASQAAAAAAAAADAAAAAAAAAEcAAABFAAAASQAAAAAAAABKAAAAAAAAAEoAAAAAAAAABAAAAAUAAAAEAAAARAAAAAYAAAAAAAAASwAAAEwAAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAHAAAAM0AAAAUAAAADQAAAM8AAAAAAAAAAAAAAE0AAABOAAAATwAAAAAAAAAUAAAAAQAEAAEAAAAAAAAAYQAAABQAAABhAAAAFAAAAAAAAAAAAAAAUAAAAAEAAAABAAAAAAAAAFkAAAAUAAAAWQAAABQAAAAAAAAAAAAAAEgAAAABAAAAAAAAAAAAAABhAAAAFAAAAGEAAAAUAAAAAAAAAAAAAABAAAAAAQABAAEAAAAAAAAAWQAAABwAAABdAAAADAAAAAAAAAAAAAAASAAAAAEAAAABAAAAAAAAAFUAAAAcAAAAWQAAAAwAAAAAAAAAAAAAAEQAAAABAAAAAgAAAAAAAABOAAAATQAAAFAAAABRAAAAAgAAAAAAAABQAAAATwAAAFIAAABTAAAABgAAAAAAAAAAAAAAAgABAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAFMAAABRAAAAVAAAAFUAAAADAAAAAAAAAFIAAABPAAAAVAAAAAAAAABWAAAAAAAAAAMAAAAAAAAAVgAAAE0AAABVAAAAAAAAAFcAAAAAAAAAVwAAAE8AAAACAAAAAAAAAEsAAAAGAAAAWAAAAFkAAAAEAAAAAAAAAAAAAAAAAAAAWQAAAAAAAABaAAAAWwAAAAMAAAAAAAAAWAAAAAYAAABaAAAAAAAAAFwAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAkAAAAGAAAAWwAAAAAAAABdAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAEAAAAAAAAAXAAAAAAAAABdAAAAAAAAAHgBAAAAAAIA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAOwCAAACAAAAAAAAAAAAAADsAgAAAgAAAAAAAAAAAAAA7AIAAAIAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAQAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAAEAAEAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAEAAQADAAAAAAAAAAAAAAABAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQABAAMAAAAAAAAAAAAAAAMAAQABAAAAAQAAAAAAAABtcXQtY2MAADQuMC4wAAAA" + }, + { + "file": "jeff/upstream/example-entangled-qubits.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q711" + ], + "how": "tableau", + "round_trip": true + }, + "base64": "AAAAAIUBAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAOkFAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQA6QMAAOcBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwQAAAAAAAAAAAAAaAAAAAEABAAAAAAAAAAAAAAAAAAAAAAA/QEAAAwAAAAAAAAAAAAAAPgBAAABAAEAAAAAAAAAAAAAAAAAAAAAAPUBAAAMAAAAAAAAAAAAAADwAQAAAQABAAAAAAAAAAAAAAAAAAAAAADtAQAADAAAAAAAAAAAAAAA6AEAAAEAAQAAAAAAAAAAAAAAAAAAAAAA5QEAAAwAAAAAAAAAAAAAAOABAAABAAEAAAAAAAAAAAAAAAAAAAAAAN0BAAAMAAAAAAAAAAAAAADYAQAAAQABAAAAAAAAAAAA2QEAAAwAAADZAQAADAAAAAAAAAAAAAAA1AEAAAEAAQAAAAAAAAAAAOEBAAAUAAAA4QEAABQAAAAAAAAAAAAAANwBAAABAAEAAAAAAAAAAADpAQAAFAAAAOkBAAAUAAAAAAAAAAAAAADkAQAAAQABAAAAAAAAAAAA8QEAABQAAADxAQAAFAAAAAAAAAAAAAAA7AEAAAEAAQAAAAAAAAAAAPkBAAAUAAAA+QEAABQAAAAAAAAAAAAAAPQBAAABAAEAAwAAAAAAAAAAAAAAAAAAAP0BAAAMAAAAAAAAAAAAAAD4AQAAAQABAAAAAAAAAAAA/QEAAAwAAAD9AQAADAAAAAAAAAAAAAAA+AEAAAEAAQACAAAAAAAAAAAAAAAAAAAA9QEAAAwAAAAAAAAAAAAAAPABAAACAAAAAwAAAAAAAADxAQAAHAAAAPUBAAAMAAAAAAAAAAAAAADwAQAAAQABAAAAAAAAAAAA8QEAAAwAAADxAQAADAAAAAAAAAAAAAAA7AEAAAEAAQACAAAAAAAAAAAAAAAAAAAA6QEAAAwAAAAAAAAAAAAAAOQBAAACAAAAAwAAAAAAAADlAQAAHAAAAOkBAAAMAAAAAAAAAAAAAADkAQAAAQABAAAAAAAAAAAA5QEAAAwAAADlAQAADAAAAAAAAAAAAAAA4AEAAAEAAQACAAAAAAAAAAAAAAAAAAAA3QEAAAwAAAAAAAAAAAAAANgBAAACAAAAAwAAAAAAAADZAQAAHAAAAN0BAAAMAAAAAAAAAAAAAADYAQAAAQABAAAAAAAAAAAA2QEAAAwAAADZAQAADAAAAAAAAAAAAAAA1AEAAAEAAQACAAAAAAAAAAAAAAAAAAAA0QEAAAwAAAAAAAAAAAAAAMwBAAACAAAAAwAAAAAAAADNAQAAHAAAANEBAAAMAAAAAAAAAAAAAADMAQAAAQABAAAAAAAAAAAAzQEAAAwAAADNAQAADAAAAAAAAAAAAAAAyAEAAAEAAQACAAAAAAAAAAAAAAAAAAAAxQEAAAwAAAAAAAAAAAAAAMABAAACAAAAAwAAAAAAAADBAQAAHAAAAMUBAAAMAAAAAAAAAAAAAADAAQAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAAAAAACAAEAAQABAAEAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAEAAAAGAAAABwAAAAYAAAAAAAAAAAAAAAIAAQACAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAHAAAAAgAAAAgAAAAJAAAABgAAAAAAAAAAAAAAAgABAAIAAQABAAEAAAAAAAAAAAAAAAAAAAAAAAkAAAADAAAACgAAAAsAAAAGAAAAAAAAAAAAAAACAAEAAgABAAEAAQAAAAAAAAAAAAAAAAAAAAAACwAAAAQAAAAMAAAADQAAAAYAAAAAAAAAAAAAAAIAAQACAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAOAAAAAAAAAAAAAAAAAAAAAQAAACkAAAAAAAAAAAAAAAYAAAAAAAAADwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAADgAAABAAAAAPAAAAAAAAABEAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAEgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAEwAAAAAAAAAAAAMAAQAAAAAAAAAAAAAAEQAAABMAAAASAAAAAAAAABQAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAFQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAFgAAAAAAAAAAAAMAAgAAAAAAAAAAAAAAFAAAABYAAAAVAAAAAAAAABcAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAGAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAAAAMAAwAAAAAAAAAAAAAAFwAAABkAAAAYAAAAAAAAABoAAAAAAAAABwAAAAAAAAAAAAAAAAAAAA0AAAAAAAAAGwAAAAAAAAADAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAMABAAAAAAAAAAAAAAAGgAAABwAAAAbAAAAAAAAAB0AAAAAAAAABwAAAAAAAAAAAAAAAAAAAHgAAAAAAAIA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAA7AAAAAIAAAAAAAAAAAAAAOwAAAACAAAAAAAAAAAAAADsAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAADAAEAAQAAAAUAAAAAAAAACQAAADoAAAAJAAAAEgAAAAkAAAASAAAAcWFycmF5AABIAAAAAAAAAFgAAAAAAAAA", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// From the example's text: x on each qubit (custom X) with one control given after its target, as the schema orders a controlled gate's qubits: qubit k + 1 controls qubit k.\nqubit[5] q;\nbit[5] c;\nh q[0];\ncx q[1], q[0];\ncx q[2], q[1];\ncx q[3], q[2];\ncx q[4], q[3];\nc = measure q;\n" + }, + { + "file": "jeff/upstream/example-qubits.jeff", + "format": "jeff", + "expect": { + "notes": [ + "Q709", + "Q711", + "Q716" + ], + "how": "exact", + "round_trip": true + }, + "base64": "AAAAAJgAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAACUCAAAuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwADAAAAAAAAAAgAAAAAAAQAVQEAALcAAAAAAAAAAAAAAA0AAAAUAAAADQAAABwAAAARAAAAHwEAAAAAAAAAAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAAAAAAHAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAgQAAAAwAAAAAAAAAAAAAAHwAAAABAAEAAAAAAAAAAAB9AAAADAAAAH0AAAAMAAAAAAAAAAAAAAB4AAAAAQABAAAAAAAAAAAAhQAAAAwAAACFAAAADAAAAAAAAAAAAAAAgAAAAAEAAQAAAAAAAAAAAI0AAAAUAAAAjQAAABQAAAAAAAAAAAAAAIgAAAABAAEAAAAAAAAAAACVAAAAFAAAAJUAAAAUAAAAAAAAAAAAAACQAAAAAQABAAQAAAAAAAAAAAAAAAAAAACZAAAADAAAAAAAAAAAAAAAlAAAAAIAAAAAAAAAAAAAAJUAAAAUAAAAlQAAAAwAAAAAAAAAAAAAAJAAAAABAAEABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQABAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAcAAAAAAAAABgAAAAAAAAAAAAAAAgABAAAAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAGAAAACAAAAAkAAAAGAAAAAAAAAAAAAAACAAEAAgABAAIAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAgAAAADAAAABAAAAAYAAAAAAAAAAAAAAAIAAQACAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAABAAAAAAAAAAAA4D8JAAAACgAAAAIAAAAAAAAABgAAAAAAAAAAAAAAAgABAAQAAQABAQAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAIAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAABAAAAAAAAAAAAAAAAABEAAAASAAAAEQAAABIAAAARAAAAGgAAABEAAABCAAAAEQAAABoAAABIAAAAAAAAAFgAAAAAAAAAQ1gAAAAAAABDaXJjdWl0AFJ4AAAAAAAA", + "reference": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\n// From the example's text: two qubit arguments and one allocated; custom X, H, CX (control first, as its name says) and Rx.\nqubit[3] q;\nx q[0];\nh q[1];\ncx q[2], q[0];\ncx q[1], q[2];\nrx(0.5) q[0];\n" + }, + { + "file": "jeff/upstream/example-entangled-calls.jeff", + "format": "jeff", + "expect": { + "refused": "Q714", + "at": "operation 29" + }, + "base64": "AAAAAM4CAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAhwAAANEKAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAEAAwAAAAAAAAAAADgAAAAAAAQA1QYAACcDAAAAAAAAAAAAAAEAAAAAAAAA7AkAAAAABAAZCgAAFwAAAAAAAAAAAAAAAgAAAAAAAAAgCgAAAAAEAFUKAAAXAAAAAAAAAAAAAAADAAAAAAAAAFwKAAAAAAQAcQoAABcAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAwAAAAJAAAANwcAAAAAAAAAAAAAMQAAAAAAAAC4AAAAAQAEAAIAAAAAAAAAAAAAAAAAAACNAwAADAAAAAAAAAAAAAAAiAMAAAIAAAAAAAAAAAAAAAAAAAAAAAAAhQMAAAwAAAAAAAAAAAAAAIADAAABAAEAAAAAAAAAAAAAAAAAAAAAAH0DAAAMAAAAAAAAAAAAAAB4AwAAAQABAAAAAAAAAAAAAAAAAAAAAAB1AwAADAAAAAAAAAAAAAAAcAMAAAEAAQAAAAAAAAAAAAAAAAAAAAAAbQMAAAwAAAAAAAAAAAAAAGgDAAABAAEAAAAAAAAAAAAAAAAAAAAAAGUDAAAMAAAAAAAAAAAAAABgAwAAAQABAAAAAAAAAAAAYQMAAAwAAABhAwAADAAAAAAAAAAAAAAAXAMAAAEAAQAAAAAAAAAAAGkDAAAUAAAAaQMAABQAAAAAAAAAAAAAAGQDAAABAAEAAAAAAAAAAABxAwAAFAAAAHEDAAAUAAAAAAAAAAAAAABsAwAAAQABAAAAAAAAAAAAeQMAABQAAAB5AwAAFAAAAAAAAAAAAAAAdAMAAAEAAQAAAAAAAAAAAIEDAAAUAAAAgQMAABQAAAAAAAAAAAAAAHwDAAABAAEAAwAAAAAAAAAAAAAAAAAAAIUDAAAMAAAAAAAAAAAAAACAAwAAAQABAAAAAAAAAAAAhQMAAAwAAACFAwAADAAAAAAAAAAAAAAAgAMAAAEAAQACAAAAAAAAAAAAAAAAAAAAfQMAAAwAAAAAAAAAAAAAAHgDAAACAAAAAwAAAAAAAAB5AwAAHAAAAH0DAAAMAAAAAAAAAAAAAAB4AwAAAQABAAAAAAAAAAAAeQMAAAwAAAB5AwAADAAAAAAAAAAAAAAAdAMAAAEAAQACAAAAAAAAAAAAAAAAAAAAcQMAAAwAAAAAAAAAAAAAAGwDAAACAAAAAwAAAAAAAABtAwAAHAAAAHEDAAAMAAAAAAAAAAAAAABsAwAAAQABAAAAAAAAAAAAbQMAAAwAAABtAwAADAAAAAAAAAAAAAAAaAMAAAEAAQACAAAAAAAAAAAAAAAAAAAAZQMAAAwAAAAAAAAAAAAAAGADAAACAAAAAwAAAAAAAABhAwAAHAAAAGUDAAAMAAAAAAAAAAAAAABgAwAAAQABAAAAAAAAAAAAYQMAAAwAAABhAwAADAAAAAAAAAAAAAAAXAMAAAEAAQACAAAAAAAAAAAAAAAAAAAAWQMAAAwAAAAAAAAAAAAAAFQDAAACAAAAAwAAAAAAAABVAwAAHAAAAFkDAAAMAAAAAAAAAAAAAABUAwAAAQABAAAAAAAAAAAAVQMAAAwAAABVAwAADAAAAAAAAAAAAAAAUAMAAAEAAQACAAAAAAAAAAAAAAAAAAAATQMAAAwAAAAAAAAAAAAAAEgDAAACAAAAAwAAAAAAAABJAwAAHAAAAE0DAAAMAAAAAAAAAAAAAABIAwAAAQABAAIAAAAAAAAAAAAAAAAAAABFAwAADAAAAAAAAAAAAAAAQAMAAAIAAAADAAAAAAAAAEEDAAAUAAAAQQMAAAwAAAAAAAAAAAAAADwDAAABAAEAAgAAAAAAAAA9AwAAFAAAAD0DAAAMAAAAAAAAAAAAAAA4AwAAAgAAAAIAAAAAAAAAAAAAAAAAAAA1AwAADAAAAAAAAAAAAAAAMAMAAAIAAAADAAAAAAAAADEDAAAUAAAAMQMAAAwAAAAAAAAAAAAAACwDAAABAAEAAgAAAAAAAAAtAwAAFAAAAC0DAAAMAAAAAAAAAAAAAAAoAwAAAgAAAAIAAAAAAAAAKQMAABQAAAApAwAADAAAAAAAAAAAAAAAJAMAAAIAAAACAAAAAAAAAAAAAAAAAAAAIQMAAAwAAAAAAAAAAAAAABwDAAACAAAAAwAAAAAAAAAdAwAAFAAAAB0DAAAMAAAAAAAAAAAAAAAYAwAAAQABAAIAAAAAAAAAGQMAABQAAAAZAwAADAAAAAAAAAAAAAAAFAMAAAIAAAACAAAAAAAAABUDAAAUAAAAFQMAAAwAAAAAAAAAAAAAABADAAACAAAAAgAAAAAAAAAAAAAAAAAAAA0DAAAMAAAAAAAAAAAAAAAIAwAAAgAAAAMAAAAAAAAACQMAABQAAAAJAwAADAAAAAAAAAAAAAAABAMAAAEAAQACAAAAAAAAAAUDAAAUAAAABQMAAAwAAAAAAAAAAAAAAAADAAACAAAAAgAAAAAAAAABAwAAFAAAAAEDAAAMAAAAAAAAAAAAAAD8AgAAAgAAAAIAAAAAAAAAAAAAAAAAAAD5AgAADAAAAAAAAAAAAAAA9AIAAAIAAAADAAAAAAAAAPUCAAAUAAAA9QIAAAwAAAAAAAAAAAAAAPACAAABAAEAAgAAAAAAAADxAgAAFAAAAPECAAAMAAAAAAAAAAAAAADsAgAAAgAAAAIAAAAAAAAA7QIAABQAAADtAgAADAAAAAAAAAAAAAAA6AIAAAIAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAYAAAAAAAAABgAAAAAAAAAAAAAAAgABAAQAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAACAAAABwAAAAgAAAAGAAAAAAAAAAAAAAACAAEABQABAAEAAQAAAAAAAAAAAAAAAAAAAAAACAAAAAMAAAAJAAAACgAAAAYAAAAAAAAAAAAAAAIAAQAFAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAKAAAABAAAAAsAAAAMAAAABgAAAAAAAAAAAAAAAgABAAUAAQABAAEAAAAAAAAAAAAAAAAAAAAAAAwAAAAFAAAADQAAAA4AAAAGAAAAAAAAAAAAAAACAAEABQABAAEAAQAAAAAAAAAAAAAAAAAAAAAADwAAAAAAAAAAAAAAAAAAAAEAAAApAAAAAAAAAAAAAAAHAAAAAAAAABAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAADAAAAAAAAAAAAAAAAAA8AAAARAAAAEAAAAAAAAAASAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAJAAAAAAAAABMAAAAAAAAAAwAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAADAAEAAAAAAAAAAAAAABIAAAAUAAAAEwAAAAAAAAAVAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAALAAAAAAAAABYAAAAAAAAAAwAAAAAAAAAAAAAAAAAAABcAAAAAAAAAAAADAAIAAAAAAAAAAAAAABUAAAAXAAAAFgAAAAAAAAAYAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAANAAAAAAAAABkAAAAAAAAAAwAAAAAAAAAAAAAAAAAAABoAAAAAAAAAAAADAAMAAAAAAAAAAAAAABgAAAAaAAAAGQAAAAAAAAAbAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAOAAAAAAAAABwAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAB0AAAAAAAAAAAADAAQAAAAAAAAAAAAAABsAAAAdAAAAHAAAAAAAAAAeAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAeAAAAHwAAACAAAAAAAAAABgAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIQAAAAAAAAAAABsAAAAAAAAAAAAAAAAAIgAAAAAAAAAAAAMAAQAAAAAAAAAAAAAAHgAAACIAAAAjAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAhAAAAIwAAACQAAAAAAAAAAAAFAAAAAAAAAAAAAAAAACQAAAAAAAAAJQAAAAAAAAAAABsAAAAAAAAAAAAAAAAAJgAAAAAAAAAAAAMAAgAAAAAAAAAAAAAAHgAAACYAAAAnAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAlAAAAJwAAACgAAAAAAAAAAAAFAAAAAAAAAAAAAAAAACgAAAAAAAAAKQAAAAAAAAAAABsAAAAAAAAAAAAAAAAAKgAAAAAAAAAAAAMAAwAAAAAAAAAAAAAAHgAAACoAAAArAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAApAAAAKwAAACwAAAAAAAAAAAAFAAAAAAAAAAAAAAAAACwAAAAAAAAALQAAAAAAAAAAABsAAAAAAAAAAAAAAAAALgAAAAAAAAAAAAMABAAAAAAAAAAAAAAAHgAAAC4AAAAvAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAtAAAALwAAADAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAADAAAAAAAAAAMQAAAAAAAAAAABsAAAAAAAAAAAAAAAAAyAAAAAAAAgCMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAIwBAAACAAAAAAAAAAAAAACMAQAAAgAAAAAAAAAAAAAAjAEAAAIAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAMAAQABAAAABQAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAADQAAAAQAAAAJAAAABAAAAAUAAAAvAAAAAAAAAAAAAAAEAAAAAQAEAAcAAAAAAAAAAAAAAAAAAAAJAAAADAAAAAAAAAAAAAAABAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAgAEAAAAAgAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAwAAAAJAAAALwAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAQAEAAIAAAAAAAAAAAAAAAAAAAAJAAAADAAAAAAAAAAAAAAABAAAAAIAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAEAAAAAAACAAQAAAACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAADQAAAAwAAAANAAAABAAAAAkAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAQABAAAAAAAAgAEAAAAAgAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAABUAAABqAQAAKQAAALIAAAAxAAAAWgAAADUAAABiAAAAOQAAABIAAAA5AAAAEgAAAF9fbnZxcHBfX21saXJnZW5fX2Z1bmN0aW9uX3NhbXBsZS5fWjZzYW1wbGV2AAAAAF9fbnZxcHBfX21saXJnZW5fX2doegAAAF9aNnNhbXBsZXYAAAAAAABfWk4zZ2h6Y2xFdgAAAAAASAAAAAAAAABYAAAAAAAAAA==" + }, + { + "file": "jeff/upstream/example-optimisation.jeff", + "format": "jeff", + "expect": { + "refused": "Q714" + }, + "base64": "AAAAAIIAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAOUBAAAOAAAAAAAAAAAAAADlAQAACgAAAOUBAAAKAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQARQEAAIcAAAAAAAAAAAAAAA0AAAAMAAAADQAAAAQAAAAJAAAAzwAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAQAEAAAAAAAAAAAAXQAAAAQAAABZAAAADAAAAAAAAAAAAAAAVAAAAAEAAQAAAAAAAAAAAFUAAAAMAAAAVQAAAAwAAAAAAAAAAAAAAFAAAAABAAEAAgAAAAAAAABdAAAADAAAAF0AAAAMAAAAAAAAAAAAAABYAAAAAgAAAAYAAAAAAAAAWQAAABQAAABZAAAADAAAAAAAAAAAAAAAVAAAAAEAAgAAAAAAAAAAAMUAAAAMAAAAxQAAAAQAAAAAAAAAAAAAALwAAAABAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQAJAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAOAAAAAAAAAAAAAAAAAAMAAAACAAAABAAAAAAAAAAAAAAAAAAAAAUAAAAnAAAAUAAAAAAABAAEAAAAAAAEAA0AAAAMAAAADQAAAAwAAAANAAAALwAAAAAAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAQAAAABAAQAAAAAAAAAAAANAAAADAAAAA0AAAAMAAAAAAAAAAAAAAAIAAAAAQABAAUAAAAAAAAABgAAAAAAAAAGAAAAAAAAAAAAAAACAAEACQAAAAAAAAABAAAAAAAAAAAAAAAAAAAADQAAAAwAAAANAAAADAAAAA0AAAAHAAAAAAAAAAAAAAAHAAAAAAAAAAcAAAAAAAAAAAAAAAEABAAEAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAgAAAAAAACADwAAAACAAAAAAAAAAAAAAA8AAAAAgAAAAAAAAAAAAAAPAAAAAIAAAAAAAAAAAAAADwAAAACAAAAAAAAAAAAAAA8AAAAAgAAAAAAAAAAAAAAPAAAAAIAAAAAAAAAAAAAADwAAAACAAAAAAAAAAAAAAA8AAAAAgAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAKgAAAG1haW4AAAAAAAAAAAAAAAAAAAAAAAAAAA==" + }, + { + "file": "jeff/upstream/example-rewrite.jeff", + "format": "jeff", + "expect": { + "refused": "Q713", + "at": "operation 5" + }, + "base64": "AAAAAHIBAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAAJwAAAHkFAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAEAAwAAAAAAAAAAAAgAAAAAAAQAWQMAAAcCAAAAAAAAAAAAAA0AAAAEAAAACQAAABQAAAAJAAAAJwMAAAAAAAAAAAAAGwAAAB0AAABQAAAAAQAEAAIAAAAAAAAAiQEAAAQAAACFAQAADAAAAAAAAAAAAAAAgAEAAAIAAAACAAAAAAAAAIEBAAAEAAAAfQEAAAwAAAAAAAAAAAAAAHgBAAACAAAAAgAAAAAAAAB5AQAABAAAAHUBAAAMAAAAAAAAAAAAAABwAQAAAgAAAAEAAAAAAAAAcQEAAAwAAABxAQAADAAAAAAAAAAAAAAAbAEAAAEAAAABAAAAAAAAAGkBAAAUAAAAaQEAABQAAAAAAAAAAAAAAGQBAAABAAAAAQAAAAAAAABhAQAAFAAAAGEBAAAUAAAAAAAAAAAAAABcAQAAAQAAAAAAAAAAAAAAWQEAABQAAABZAQAAFAAAAAAAAAAAAAAAVAEAAAEAAQAAAAAAAAAAAGEBAAAMAAAAYQEAAAwAAAAAAAAAAAAAAFwBAAABAAEAAAAAAAAAAABpAQAAFAAAAGkBAAAUAAAAAAAAAAAAAABkAQAAAQABAAIAAAAAAAAAcQEAAAQAAABtAQAADAAAAAAAAAAAAAAAaAEAAAIAAAACAAAAAAAAAGkBAAAEAAAAZQEAAAwAAAAAAAAAAAAAAGABAAACAAAAAgAAAAAAAABhAQAABAAAAF0BAAAMAAAAAAAAAAAAAABYAQAAAgAAAAYAAAAAAAAAWQEAACwAAABhAQAAFAAAAAAAAAAAAAAAXAEAAAEAAgAAAAAAAAAAALEBAAAUAAAAsQEAABQAAAAAAAAAAAAAAKwBAAABAAEAAAAAAAAAAAC5AQAADAAAALkBAAAMAAAAAAAAAAAAAAC0AQAAAQABAAAAAAAAAAAAwQEAAAwAAADBAQAAFAAAAAAAAAAAAAAAvAEAAAEAAQAAAAAAAAAAAL0BAAAMAAAAvQEAABQAAAAAAAAAAAAAALgBAAABAAEAAQAAAAAAAAC5AQAAHAAAAL0BAAAMAAAAAAAAAAAAAAC4AQAAAQAAAAEAAAAAAAAAtQEAABwAAAC5AQAADAAAAAAAAAAAAAAAtAEAAAEAAAABAAAAAAAAALEBAAAMAAAAsQEAAAQAAAAAAAAAAAAAAKgBAAABAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAMAAgAAAAAAAAAAAAAAAgAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAEAAAABQAAAAIAAAAAAAAABAAAAAEAAAAGAAAABwAAAAIAAAAAAAAABQAAAAcAAAAIAAAACQAAAAYAAAAAAAAAAAAAAAIAAQABAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAJAAAAAAAAAAoAAAAAAAAABgAAAAAAAAAAAAAAAgABAAIAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAKAAAACwAAAAwAAAAGAAAAAAAAAAAAAAACAAEAAwABAAIAAAAAAAAAAAAAAAAAAAAAAAAADQAAAAAAAAAAAAMAAAAAAAAAAAAAAAAADgAAAAAAAAAAAAMAAwAAAAAAAAAAAAAADwAAAAAAAAAAAAMAAQAAAAAAAAAAAAAADQAAAA4AAAAPAAAACwAAAAwAAAAAAAAAFQAAABYAAAABAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAANAAAAHAAAABEAAAAUAAAAEQAAAC8AAAAAAAAAAAAAABAAAAARAAAAEgAAAAAAAAATAAAAFAAAAAQAAAABAAQAAAAAAAAAAAANAAAAFAAAAA0AAAAUAAAAAAAAAAAAAAAIAAAAAQABABEAAAASAAAAEwAAABQAAAAGAAAAAAAAAAAAAAACAAEABAABAAIAAAAAAAAAAAAAAAAAAAAAAAAAFQAAABYAAAAXAAAAGAAAAAYAAAAAAAAAAAAAAAIAAQAFAAEAAgAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAABkAAAAAAAAABgAAAAAAAAAAAAAAAgABAAYAAQABAAAAAAAAAAAAAAAAAAAAAAAAABcAAAAAAAAAGgAAABsAAAAEAAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAcAAAAHQAAAAQAAAAAAAAAAAAAAAAAAAAGAAAAAAAAABoAAAAAAAAAHgAAAAAAAAADAAAAAAAAAB4AAAABAAAAHAAAAAAAAAAfAAAAAAAAAAMAAAAAAAAAHwAAAAAAAAAKAAAAAAAAAIAAAAAAAAIA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAD8AAAAAgAAAAAAAAAAAAAA/AAAAAIAAAAAAAAAAAAAAPwAAAACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAIAIAAAAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAABkAAABiAAAAHQAAABoAAAAdAAAAEgAAAB0AAAAaAAAAHQAAACoAAAAdAAAAGgAAAB0AAAASAAAAb3B0aW1pemVfbWUAAAAAAENYAAAAAAAAVAAAAAAAAABDWAAAAAAAAFNXQVAAAAAAQ1gAAAAAAABUAAAAAAAAAA==" + }, + { + "file": "jeff/upstream/example-simple.jeff", + "format": "jeff", + "expect": { + "refused": "Q710", + "at": "operation 5" + }, + "base64": "AAAAAKwAAAAAAAAAAgAFAAAAAAAAAAAAAwAAAAAAAAARAAAARwAAAHkCAAAmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAEAAwAAAAAAAAAAABgAAAAAAAQAkQEAALcAAAAAAAAAAAAAAAMAAAAAAAAAOAIAAAAABABJAgAABwAAAAAAAAAAAAAADQAAAAQAAAAJAAAADAAAAAkAAACXAQAAAAAAAAAAAAAJAAAAAAAAACgAAAABAAQAAgAAAAAAAADBAAAABAAAAL0AAAAMAAAAAAAAAAAAAAC4AAAAAgAAAAIAAAAAAAAAuQAAAAQAAAC1AAAADAAAAAAAAAAAAAAAsAAAAAIAAAAEAAAAAAAAALEAAAAEAAAArQAAAAwAAAAAAAAAAAAAAKgAAAACAAAAAQAAAAAAAACpAAAADAAAAKkAAAAMAAAAAAAAAAAAAACkAAAAAQAAAAEAAAAAAAAAoQAAABQAAAChAAAAFAAAAAAAAAAAAAAAnAAAAAEAAAAAAAAAAAAAAJkAAAAMAAAAmQAAAAwAAAAAAAAAAAAAAJQAAAABAAEAAAAAAAAAAAChAAAAFAAAAKEAAAAMAAAAAAAAAAAAAACcAAAAAQABAAAAAAAAAAAAqQAAAAwAAACpAAAAFAAAAAAAAAAAAAAApAAAAAEAAQABAAAAAAAAAKUAAAAcAAAAqQAAAAwAAAAAAAAAAAAAAKQAAAABAAAAAQAAAAAAAAChAAAADAAAAKEAAAAEAAAAAAAAAAAAAACYAAAAAQAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAADAAUAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAEAAACamZmZmZm5PwEAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAAAAAABAAAAAUAAAACAAAAAAAAAAUAAAAAAAAABgAAAAAAAAAGAAAAAAAAAAAAAAACAAEAAQABAAEAAAABAAAAAAAAAAAAAAAAAAAABgAAAAIAAAAHAAAAAAAAAAYAAAAAAAAAAAAAAAIAAQACAAEAAQEAAAEAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAgAAAAJAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAACAAAAAAAAAAKAAAAAAAAAAMAAAAAAAAACgAAAAAAAAAKAAAAAAAAACwAAAAAAAIAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAABUAAAAAgAAAAAAAAAAAAAAVAAAAAIAAAAAAAAAAAAAAFQAAAACAAAAAAAAAAAAAAACACAAAAAAAAAAAAAAAAAAAgAgAAAAAAAAAAAAAAAAAAQAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA0AAAAEAAAACQAAAAQAAAAFAAAABwAAAAAAAAAAAAAAAAAAAAEABAAAAAAAAAACAA0AAAAyAAAADQAAAEoAAAARAAAAGgAAABEAAAAyAAAAaGVsbG8AAABIYWRhbWFyZAAAAAAAAAAAUlkAAAAAAAB3b3JsZAAAAA==" + }, + { + "file": "qasm/every_gate.qasm", + "format": "qasm", + "expect": { + "writes": [ + { + "to": "qir", + "profile": "base", + "how": "exact", + "blake3": "1ec96352d855778f5a41c09d96fd0d260632663f03213e225255f074e8a4c022" + }, + { + "to": "jeff", + "how": "exact" + } + ] + }, + "text": "// Every gate of the language once, on qubits put in superpositions first,\n// then every qubit measured: written to QIR and jeff, each read back the same\n// circuit.\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nry(0.37) q[1];\nrx(1.1) q[2];\nid 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];\nrx(0.3) q[0]; ry(-0.7) q[1]; rz(1.9) q[2]; p(0.45) q[0]; p(pi/4) q[1]; p(-pi/2) q[2]; U(0.2, 0.9, -1.4) q[1];\ncx q[0], q[1]; cy q[1], q[2]; cz q[2], q[0]; ch q[0], q[2]; cp(0.8) q[1], q[0];\ncrx(0.6) q[0], q[1]; cry(-1.2) q[2], q[1]; crz(2.2) q[1], q[2]; cu(0.3, 0.4, 0.5, 0.6) q[0], q[2];\nswap q[0], q[2]; iswap q[1], q[0]; rxx(0.7) q[0], q[1]; ryy(1.1) q[1], q[2]; rzz(-0.4) q[2], q[0];\nccx q[0], q[1], q[2]; ccz q[2], q[0], q[1]; cswap q[1], q[0], q[2];\nc = measure q;\n" + }, + { + "file": "qasm/clifford_t.qasm", + "format": "qasm", + "expect": { + "writes": [ + { + "to": "qir", + "profile": "base", + "how": "exact", + "blake3": "967659b446471c62697f49df341972a45f47bef59d3b2fe26bfefc6670f6a50d" + }, + { + "to": "jeff", + "how": "exact" + } + ] + }, + "text": "// A Clifford+T program: written to QIR in Clifford+T intrinsics alone, no\n// rotation, and read back to the bit-exact method.\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\np(pi/4) q[1];\np(3*pi/4) q[2];\ncx q[0], q[1];\nccx q[0], q[1], q[2];\ns q[2];\nc = measure q;\n" + }, + { + "file": "qasm/teleport.qasm", + "format": "qasm", + "expect": { + "dynamic": true, + "outcomes": [ + "100", + "101", + "110", + "111" + ], + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "blake3": "4ac7bea67cb849aac1093b126a2da4a1edc5240176b6d0f42579760713d3c36f" + }, + { + "to": "qir", + "profile_asked": "base", + "refused": "Q720" + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + }, + "text": "// Teleportation of |1>, corrected on what was measured: written to QIR under\n// the adaptive profile, every shot reads 1 on c[2]. jeff writes no branch.\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nx q[0];\nh q[1];\ncx q[1], q[2];\ncx q[0], q[1];\nh q[0];\nc[0] = measure q[0];\nc[1] = measure q[1];\nif (c[1]) {\n x q[2];\n}\nif (c[0] == 1) {\n z q[2];\n}\nc[2] = measure q[2];\n" + }, + { + "file": "qasm/measure_reset.qasm", + "format": "qasm", + "expect": { + "dynamic": true, + "outcomes": [ + "010", + "111" + ], + "writes": [ + { + "to": "jeff" + }, + { + "to": "qir", + "profile": "adaptive", + "blake3": "fcb9b8d9517b3e76ae4f0a800222ca1624cc59d6a594f51781515d661bd169c2" + } + ] + }, + "text": "// A measurement in the middle and a reset, with no branch: written to jeff\n// with measureNd and reset, and to QIR under the adaptive profile.\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[3] c;\nh q[0];\ncx q[0], q[1];\nc[0] = measure q[0];\nreset q[0];\nx q[0];\nc[1] = measure q[0];\nc[2] = measure q[1];\n" + }, + { + "file": "qasm/timing.qasm", + "format": "qasm", + "expect": { + "outcomes": [ + "00", + "11" + ], + "writes": [ + { + "to": "qir", + "profile": "base", + "notes": [ + "Q721" + ] + }, + { + "to": "jeff", + "notes": [ + "Q721" + ] + } + ] + }, + "text": "// A barrier and a delay: neither format has a place for them, so they are\n// left out, and the conversion says so.\nOPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\nbarrier q;\ndelay[20ns] q[1];\ncx q[0], q[1];\nc = measure q;\n" + } + ] +} diff --git a/wai/quantum-conformance/formats/jeff/controls_after_targets.jeff b/wai/quantum-conformance/formats/jeff/controls_after_targets.jeff new file mode 100644 index 000000000..82c75d98c Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/controls_after_targets.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/controls_after_targets.ref.qasm b/wai/quantum-conformance/formats/jeff/controls_after_targets.ref.qasm new file mode 100644 index 000000000..9028c604e --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/controls_after_targets.ref.qasm @@ -0,0 +1,10 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +h q[0]; +cx q[0], q[1]; +rx(-0.9) q[2]; +sdg q[0]; +U(0.2, 0.4, -0.6) q[1]; +ctrl(2) @ p(0.9) q[0], q[1], q[2]; +p(0.25) q[0]; diff --git a/wai/quantum-conformance/formats/jeff/custom_named.jeff b/wai/quantum-conformance/formats/jeff/custom_named.jeff new file mode 100644 index 000000000..138d5b2e4 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/custom_named.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/custom_named.ref.qasm b/wai/quantum-conformance/formats/jeff/custom_named.ref.qasm new file mode 100644 index 000000000..96207b931 --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/custom_named.ref.qasm @@ -0,0 +1,6 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[2] q; +h q[0]; +cx q[0], q[1]; +rx(0.5) q[1]; diff --git a/wai/quantum-conformance/formats/jeff/pauli_product.jeff b/wai/quantum-conformance/formats/jeff/pauli_product.jeff new file mode 100644 index 000000000..6956a311a Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/pauli_product.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/pauli_product.ref.qasm b/wai/quantum-conformance/formats/jeff/pauli_product.ref.qasm new file mode 100644 index 000000000..644902aab --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/pauli_product.ref.qasm @@ -0,0 +1,15 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// exp(0.37i X⊗Y⊗Z), with its parity carried along a chain rather than gathered on the last qubit. +qubit[3] q; +h q[0]; +sdg q[1]; +h q[1]; +cx q[0], q[1]; +cx q[1], q[2]; +rz(-0.74) q[2]; +cx q[1], q[2]; +cx q[0], q[1]; +h q[1]; +s q[1]; +h q[0]; diff --git a/wai/quantum-conformance/formats/jeff/refuse/angle_from_measurement.jeff b/wai/quantum-conformance/formats/jeff/refuse/angle_from_measurement.jeff new file mode 100644 index 000000000..bfcfbe48d Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/refuse/angle_from_measurement.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/refuse/custom_unknown.jeff b/wai/quantum-conformance/formats/jeff/refuse/custom_unknown.jeff new file mode 100644 index 000000000..b23b6fb86 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/refuse/custom_unknown.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/refuse/custom_wrong_qubits.jeff b/wai/quantum-conformance/formats/jeff/refuse/custom_wrong_qubits.jeff new file mode 100644 index 000000000..7ec2f84e8 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/refuse/custom_wrong_qubits.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/refuse/old_version.jeff b/wai/quantum-conformance/formats/jeff/refuse/old_version.jeff new file mode 100644 index 000000000..2b13893d2 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/refuse/old_version.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/refuse/qubit_twice.jeff b/wai/quantum-conformance/formats/jeff/refuse/qubit_twice.jeff new file mode 100644 index 000000000..5d82f0db7 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/refuse/qubit_twice.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/refuse/register_too_large.jeff b/wai/quantum-conformance/formats/jeff/refuse/register_too_large.jeff new file mode 100644 index 000000000..bf2a4c0dc Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/refuse/register_too_large.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/refuse/truncated.jeff b/wai/quantum-conformance/formats/jeff/refuse/truncated.jeff new file mode 100644 index 000000000..5dd0fcb0b Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/refuse/truncated.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/controlled-multiplication-modulo-n_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/controlled-multiplication-modulo-n_3.jeff new file mode 100644 index 000000000..0cbcdbc34 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/controlled-multiplication-modulo-n_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-entangled-calls.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-entangled-calls.jeff new file mode 100644 index 000000000..7d1a683a0 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/example-entangled-calls.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.jeff new file mode 100644 index 000000000..b64ddb2a8 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.ref.qasm b/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.ref.qasm new file mode 100644 index 000000000..0f169c58a --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/upstream/example-entangled-qubits.ref.qasm @@ -0,0 +1,11 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// From the example's text: x on each qubit (custom X) with one control given after its target, as the schema orders a controlled gate's qubits: qubit k + 1 controls qubit k. +qubit[5] q; +bit[5] c; +h q[0]; +cx q[1], q[0]; +cx q[2], q[1]; +cx q[3], q[2]; +cx q[4], q[3]; +c = measure q; diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-optimisation.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-optimisation.jeff new file mode 100644 index 000000000..a1f1c4a2d Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/example-optimisation.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-qubits.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-qubits.jeff new file mode 100644 index 000000000..66e6e60c5 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/example-qubits.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-qubits.ref.qasm b/wai/quantum-conformance/formats/jeff/upstream/example-qubits.ref.qasm new file mode 100644 index 000000000..0c07932a9 --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/upstream/example-qubits.ref.qasm @@ -0,0 +1,9 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// From the example's text: two qubit arguments and one allocated; custom X, H, CX (control first, as its name says) and Rx. +qubit[3] q; +x q[0]; +h q[1]; +cx q[2], q[0]; +cx q[1], q[2]; +rx(0.5) q[0]; diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-rewrite.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-rewrite.jeff new file mode 100644 index 000000000..3ce229074 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/example-rewrite.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/example-simple.jeff b/wai/quantum-conformance/formats/jeff/upstream/example-simple.jeff new file mode 100644 index 000000000..e8db19e62 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/example-simple.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.jeff new file mode 100644 index 000000000..5e280f14d Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.ref.qasm b/wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.ref.qasm new file mode 100644 index 000000000..d74da1f91 --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/upstream/ghz-linear_3.ref.qasm @@ -0,0 +1,9 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// From the benchmark's source: h on the first qubit, then each qubit controls x on the next. +qubit[3] q; +bit[3] c; +h q[0]; +cx q[0], q[1]; +cx q[1], q[2]; +c = measure q; diff --git a/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.jeff new file mode 100644 index 000000000..e3a5722db Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.ref.qasm b/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.ref.qasm new file mode 100644 index 000000000..af5082c23 --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/upstream/ghz-star_3.ref.qasm @@ -0,0 +1,9 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// From the benchmark's source: h on the first qubit, which controls x on each other. +qubit[3] q; +bit[3] c; +h q[0]; +cx q[0], q[1]; +cx q[0], q[2]; +c = measure q; diff --git a/wai/quantum-conformance/formats/jeff/upstream/grover_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/grover_3.jeff new file mode 100644 index 000000000..39a785934 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/grover_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/iqft_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/iqft_3.jeff new file mode 100644 index 000000000..289c50f4f Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/iqft_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/iqpe_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/iqpe_3.jeff new file mode 100644 index 000000000..886149e8e Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/iqpe_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/multiplexer_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/multiplexer_3.jeff new file mode 100644 index 000000000..89e7eca05 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/multiplexer_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/qft-adder-classical_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/qft-adder-classical_3.jeff new file mode 100644 index 000000000..e8c63dbe5 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/qft-adder-classical_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/qft-adder-quantum_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/qft-adder-quantum_3.jeff new file mode 100644 index 000000000..955bb7e7d Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/qft-adder-quantum_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/qft_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/qft_3.jeff new file mode 100644 index 000000000..7b525dcc5 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/qft_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/qft_3.ref.qasm b/wai/quantum-conformance/formats/jeff/upstream/qft_3.ref.qasm new file mode 100644 index 000000000..bc88a2464 --- /dev/null +++ b/wai/quantum-conformance/formats/jeff/upstream/qft_3.ref.qasm @@ -0,0 +1,14 @@ +OPENQASM 3.0; +include "stdgates.inc"; +// From the benchmark's source: the textbook QFT on three qubits, qubit i measured into bit 2 - i. +qubit[3] q; +bit[3] c; +h q[2]; +cp(pi/2) q[1], q[2]; +cp(pi/4) q[0], q[2]; +h q[1]; +cp(pi/2) q[0], q[1]; +h q[0]; +c[2] = measure q[0]; +c[1] = measure q[1]; +c[0] = measure q[2]; diff --git a/wai/quantum-conformance/formats/jeff/upstream/qpe_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/qpe_3.jeff new file mode 100644 index 000000000..e04782527 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/qpe_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/repeat-until-success_3.jeff b/wai/quantum-conformance/formats/jeff/upstream/repeat-until-success_3.jeff new file mode 100644 index 000000000..ec4148be8 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/repeat-until-success_3.jeff differ diff --git a/wai/quantum-conformance/formats/jeff/upstream/teleportation.jeff b/wai/quantum-conformance/formats/jeff/upstream/teleportation.jeff new file mode 100644 index 000000000..fea132b30 Binary files /dev/null and b/wai/quantum-conformance/formats/jeff/upstream/teleportation.jeff differ diff --git a/wai/quantum-conformance/formats/manifest.json b/wai/quantum-conformance/formats/manifest.json new file mode 100644 index 000000000..598ff191d --- /dev/null +++ b/wai/quantum-conformance/formats/manifest.json @@ -0,0 +1,856 @@ +{ + "format": "wai.formats-conformance/1", + "about": "Programs in other formats, each with what the engine must do with it: QIR modules (base and adaptive profiles) and jeff modules read, with the program each reads to and the refusals with their codes and places; OpenQASM sources written to each format and read back. jeff/upstream/ holds the jeff project's own structured benchmarks and examples, unchanged (Apache-2.0); the QIR specification's two example programs are its own (MIT).", + "expectations": { + "refused": "reading refuses the module with this code; `line` is the QIR line it names, `at` a phrase of where in a jeff module (a function and an operation)", + "notes": "the codes of every warning and note the reader gives, sorted (none when empty)", + "profile": "the QIR profile it is read under", + "dynamic": "whether the program read measures in the middle, resets or branches", + "qubits": "the qubits of the program read", + "reference": "(beside expect) an OpenQASM program the reading must be the same circuit as, by the equivalence ladder; `how` the check that must decide it", + "exact": "the bit-exact method takes the program read", + "outcomes": "the outcomes 512 shots give (seed 7), every one of them and no other", + "equal_bits": "pairs of bits equal in every outcome", + "round_trip": "written back to its format and read again, the two readings the same by the ladder (or by every outcome's probability, for a program that branches)", + "writes": "converted to each format named, the output read back and held to the program: the same (`how` the check that decides it), under a `profile`, with these `notes`, its bytes' BLAKE3 for QIR, or `refused` with a code (`profile_asked` asks for a profile)" + }, + "cases": [ + { + "file": "qir/bell_base.ll", + "format": "qir", + "covers": [ + "the base profile's own example", + "output records with labels" + ], + "reference": "qir/bell.ref.qasm", + "expect": { + "profile": "base", + "notes": [], + "how": "tableau", + "outcomes": [ + "00", + "11" + ], + "round_trip": true + } + }, + { + "file": "qir/every_intrinsic.ll", + "format": "qir", + "covers": [ + "every intrinsic the reader takes", + "angles in decimal, with an exponent and in hex", + "__adj forms" + ], + "reference": "qir/every_intrinsic.ref.qasm", + "expect": { + "profile": "base", + "notes": [ + "Q709" + ], + "how": "exact", + "round_trip": true + } + }, + { + "file": "qir/clifford_t.ll", + "format": "qir", + "covers": [ + "a Clifford+T module on the bit-exact method", + "pi/4 as its exact hex double" + ], + "reference": "qir/clifford_t.ref.qasm", + "expect": { + "profile": "base", + "notes": [], + "how": "exact", + "exact": true, + "round_trip": true + } + }, + { + "file": "qir/qft3_typed.ll", + "format": "qir", + "covers": [ + "QIR 1.0 with typed pointers, from the crate's earlier writer", + "controlled phases as rz and cnot" + ], + "reference": "qir/qft3_typed.ref.qasm", + "expect": { + "profile": "base", + "notes": [], + "how": "exact", + "exact": true, + "round_trip": true + } + }, + { + "file": "qir/teleport_chain.ll", + "format": "qir", + "covers": [ + "the adaptive profile's own example", + "measurements in the middle, resets, forward branches on results" + ], + "expect": { + "profile": "adaptive", + "notes": [], + "dynamic": true, + "qubits": 6, + "equal_bits": [ + [ + 4, + 5 + ] + ], + "round_trip": true + } + }, + { + "file": "qir/computed.ll", + "format": "qir", + "covers": [ + "float computation the flags declare, computed on reading", + "i1 logic on results as a condition" + ], + "expect": { + "profile": "adaptive", + "notes": [ + "Q709" + ], + "dynamic": true, + "outcomes": [ + "001", + "010", + "100", + "111" + ], + "round_trip": true + } + }, + { + "file": "qir/no_profile.ll", + "format": "qir", + "covers": [ + "a module that names no profile" + ], + "expect": { + "profile": "base", + "notes": [ + "Q702" + ], + "outcomes": [ + "0", + "1" + ] + } + }, + { + "file": "qir/refuse/base_branch.ll", + "format": "qir", + "covers": [ + "reading a result under the base profile" + ], + "expect": { + "refused": "Q704", + "line": 12 + } + }, + { + "file": "qir/refuse/base_gate_after_measure.ll", + "format": "qir", + "covers": [ + "a gate after a measurement under the base profile" + ], + "expect": { + "refused": "Q704", + "line": 11 + } + }, + { + "file": "qir/refuse/undeclared_float.ll", + "format": "qir", + "covers": [ + "float computation without its module flag" + ], + "expect": { + "refused": "Q704", + "line": 6 + } + }, + { + "file": "qir/refuse/loop_undeclared.ll", + "format": "qir", + "covers": [ + "a loop without backwards_branching" + ], + "expect": { + "refused": "Q704", + "line": 10 + } + }, + { + "file": "qir/refuse/loop_declared.ll", + "format": "qir", + "covers": [ + "a loop, declared" + ], + "expect": { + "refused": "Q705", + "line": 10 + } + }, + { + "file": "qir/refuse/switch_declared.ll", + "format": "qir", + "covers": [ + "switch, declared" + ], + "expect": { + "refused": "Q705", + "line": 8 + } + }, + { + "file": "qir/refuse/dynamic_qubits.ll", + "format": "qir", + "covers": [ + "dynamic qubit management, declared" + ], + "expect": { + "refused": "Q705", + "line": 6 + } + }, + { + "file": "qir/refuse/stale_read.ll", + "format": "qir", + "covers": [ + "a result read, measured again, then branched on" + ], + "expect": { + "refused": "Q708", + "line": 9 + } + }, + { + "file": "qir/refuse/unknown_intrinsic.ll", + "format": "qir", + "covers": [ + "an intrinsic the reader does not know" + ], + "expect": { + "refused": "Q706", + "line": 8 + } + }, + { + "file": "qir/refuse/qubit_past_count.ll", + "format": "qir", + "covers": [ + "a qubit past required_num_qubits" + ], + "expect": { + "refused": "Q707", + "line": 8 + } + }, + { + "file": "qir/refuse/unknown_profile.ll", + "format": "qir", + "covers": [ + "a profile the reader does not take" + ], + "expect": { + "refused": "Q703", + "line": 3 + } + }, + { + "file": "qir/refuse/major_version.ll", + "format": "qir", + "covers": [ + "QIR 3" + ], + "expect": { + "refused": "Q701" + } + }, + { + "file": "qir/refuse/not_llvm.ll", + "format": "qir", + "covers": [ + "not LLVM text" + ], + "expect": { + "refused": "Q700" + } + }, + { + "file": "jeff/controls_after_targets.jeff", + "format": "jeff", + "covers": [ + "well-known gates, controls after targets as the schema orders them", + "adjoint and power", + "r1, u and gphase under a control" + ], + "reference": "jeff/controls_after_targets.ref.qasm", + "expect": { + "notes": [ + "Q709" + ], + "how": "exact", + "round_trip": true + } + }, + { + "file": "jeff/custom_named.jeff", + "format": "jeff", + "covers": [ + "custom gates named as standard gates" + ], + "reference": "jeff/custom_named.ref.qasm", + "expect": { + "notes": [ + "Q709", + "Q711" + ], + "how": "exact", + "round_trip": true + } + }, + { + "file": "jeff/pauli_product.jeff", + "format": "jeff", + "covers": [ + "a Pauli-product rotation exp(iθP) on three qubits" + ], + "reference": "jeff/pauli_product.ref.qasm", + "expect": { + "notes": [ + "Q709", + "Q712" + ], + "how": "exact", + "round_trip": true + } + }, + { + "file": "jeff/refuse/custom_unknown.jeff", + "format": "jeff", + "covers": [ + "a custom gate the library does not name" + ], + "expect": { + "refused": "Q710", + "at": "operation 1" + } + }, + { + "file": "jeff/refuse/custom_wrong_qubits.jeff", + "format": "jeff", + "covers": [ + "a library name with the wrong qubits" + ], + "expect": { + "refused": "Q710", + "at": "operation 1" + } + }, + { + "file": "jeff/refuse/qubit_twice.jeff", + "format": "jeff", + "covers": [ + "a qubit used twice" + ], + "expect": { + "refused": "Q713", + "at": "operation 2" + } + }, + { + "file": "jeff/refuse/old_version.jeff", + "format": "jeff", + "covers": [ + "jeff 0.1" + ], + "expect": { + "refused": "Q701" + } + }, + { + "file": "jeff/refuse/angle_from_measurement.jeff", + "format": "jeff", + "covers": [ + "an angle computed from a measured bit" + ], + "expect": { + "refused": "Q714", + "at": "operation 4" + } + }, + { + "file": "jeff/refuse/truncated.jeff", + "format": "jeff", + "covers": [ + "a module cut short" + ], + "expect": { + "refused": "Q700" + } + }, + { + "file": "jeff/refuse/register_too_large.jeff", + "format": "jeff", + "covers": [ + "a register of 2^32 - 1 qubits, refused before it is allocated" + ], + "expect": { + "refused": "Q111", + "at": "operation 1" + } + }, + { + "file": "jeff/upstream/ghz-linear_3.jeff", + "format": "jeff", + "covers": [ + "a for loop over a register's indices, unrolled", + "bits returned as an array" + ], + "reference": "jeff/upstream/ghz-linear_3.ref.qasm", + "expect": { + "notes": [ + "Q715" + ], + "how": "tableau", + "outcomes": [ + "000", + "111" + ], + "round_trip": true + } + }, + { + "file": "jeff/upstream/ghz-star_3.jeff", + "format": "jeff", + "covers": [ + "a for loop, unrolled" + ], + "reference": "jeff/upstream/ghz-star_3.ref.qasm", + "expect": { + "notes": [ + "Q715" + ], + "how": "tableau", + "outcomes": [ + "000", + "111" + ], + "round_trip": true + } + }, + { + "file": "jeff/upstream/qft_3.jeff", + "format": "jeff", + "covers": [ + "nested loops with an angle carried as loop state", + "bits returned in reverse order" + ], + "reference": "jeff/upstream/qft_3.ref.qasm", + "expect": { + "notes": [ + "Q715" + ], + "how": "exact", + "round_trip": true + } + }, + { + "file": "jeff/upstream/grover_3.jeff", + "format": "jeff", + "covers": [ + "Grover's search on two qubits, one iteration" + ], + "expect": { + "notes": [ + "Q715" + ], + "outcomes": [ + "11" + ], + "round_trip": true + } + }, + { + "file": "jeff/upstream/teleportation.jeff", + "format": "jeff", + "covers": [ + "a switch on a measured bit, written as a condition", + "written to QIR's adaptive profile; jeff writes no branch" + ], + "expect": { + "notes": [ + "Q715" + ], + "dynamic": true, + "outcomes": [ + "000", + "010", + "100", + "110" + ], + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "notes": [ + "Q715" + ] + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + } + }, + { + "file": "jeff/upstream/iqft_3.jeff", + "format": "jeff", + "covers": [ + "a qubit reused after a measurement and a reset", + "switches on measured bits", + "written to QIR's adaptive profile; jeff writes no branch" + ], + "expect": { + "notes": [ + "Q715" + ], + "dynamic": true, + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "notes": [ + "Q715" + ] + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + } + }, + { + "file": "jeff/upstream/iqpe_3.jeff", + "format": "jeff", + "covers": [ + "a qubit reused after a measurement and a reset", + "switches on measured bits", + "written to QIR's adaptive profile; jeff writes no branch" + ], + "expect": { + "notes": [ + "Q715" + ], + "dynamic": true, + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "notes": [ + "Q715" + ] + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + } + }, + { + "file": "jeff/upstream/qpe_3.jeff", + "format": "jeff", + "covers": [ + "structured loops and registers, read and written back" + ], + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + } + }, + { + "file": "jeff/upstream/multiplexer_3.jeff", + "format": "jeff", + "covers": [ + "structured loops and registers, read and written back" + ], + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + } + }, + { + "file": "jeff/upstream/qft-adder-classical_3.jeff", + "format": "jeff", + "covers": [ + "structured loops and registers, read and written back" + ], + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + } + }, + { + "file": "jeff/upstream/qft-adder-quantum_3.jeff", + "format": "jeff", + "covers": [ + "structured loops and registers, read and written back" + ], + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + } + }, + { + "file": "jeff/upstream/controlled-multiplication-modulo-n_3.jeff", + "format": "jeff", + "covers": [ + "structured loops and registers, read and written back" + ], + "expect": { + "notes": [ + "Q715" + ], + "dynamic": false, + "round_trip": true + } + }, + { + "file": "jeff/upstream/repeat-until-success_3.jeff", + "format": "jeff", + "covers": [ + "a while loop on a measurement" + ], + "expect": { + "refused": "Q714", + "at": "operation 10" + } + }, + { + "file": "jeff/upstream/example-entangled-qubits.jeff", + "format": "jeff", + "covers": [ + "custom gates named as standard gates, a control given after its target" + ], + "reference": "jeff/upstream/example-entangled-qubits.ref.qasm", + "expect": { + "notes": [ + "Q711" + ], + "how": "tableau", + "round_trip": true + } + }, + { + "file": "jeff/upstream/example-qubits.jeff", + "format": "jeff", + "covers": [ + "an entry point that takes qubits" + ], + "reference": "jeff/upstream/example-qubits.ref.qasm", + "expect": { + "notes": [ + "Q709", + "Q711", + "Q716" + ], + "how": "exact", + "round_trip": true + } + }, + { + "file": "jeff/upstream/example-entangled-calls.jeff", + "format": "jeff", + "covers": [ + "arithmetic on measured bits" + ], + "expect": { + "refused": "Q714", + "at": "operation 29" + } + }, + { + "file": "jeff/upstream/example-optimisation.jeff", + "format": "jeff", + "covers": [ + "an entry point that takes a classical argument" + ], + "expect": { + "refused": "Q714" + } + }, + { + "file": "jeff/upstream/example-rewrite.jeff", + "format": "jeff", + "covers": [ + "a register's slot extracted twice" + ], + "expect": { + "refused": "Q713", + "at": "operation 5" + } + }, + { + "file": "jeff/upstream/example-simple.jeff", + "format": "jeff", + "covers": [ + "a custom gate the library does not name" + ], + "expect": { + "refused": "Q710", + "at": "operation 5" + } + }, + { + "file": "qasm/every_gate.qasm", + "format": "qasm", + "covers": [ + "every gate of the language, written to each format" + ], + "expect": { + "writes": [ + { + "to": "qir", + "profile": "base", + "how": "exact", + "blake3": "1ec96352d855778f5a41c09d96fd0d260632663f03213e225255f074e8a4c022" + }, + { + "to": "jeff", + "how": "exact" + } + ] + } + }, + { + "file": "qasm/clifford_t.qasm", + "format": "qasm", + "covers": [ + "Clifford+T written as Clifford+T intrinsics" + ], + "expect": { + "writes": [ + { + "to": "qir", + "profile": "base", + "how": "exact", + "blake3": "967659b446471c62697f49df341972a45f47bef59d3b2fe26bfefc6670f6a50d" + }, + { + "to": "jeff", + "how": "exact" + } + ] + } + }, + { + "file": "qasm/teleport.qasm", + "format": "qasm", + "covers": [ + "a branching program under the adaptive profile", + "jeff writes no branch" + ], + "expect": { + "dynamic": true, + "outcomes": [ + "100", + "101", + "110", + "111" + ], + "writes": [ + { + "to": "qir", + "profile": "adaptive", + "blake3": "4ac7bea67cb849aac1093b126a2da4a1edc5240176b6d0f42579760713d3c36f" + }, + { + "to": "qir", + "profile_asked": "base", + "refused": "Q720" + }, + { + "to": "jeff", + "refused": "Q720" + } + ] + } + }, + { + "file": "qasm/measure_reset.qasm", + "format": "qasm", + "covers": [ + "a measurement in the middle and a reset" + ], + "expect": { + "dynamic": true, + "outcomes": [ + "010", + "111" + ], + "writes": [ + { + "to": "jeff" + }, + { + "to": "qir", + "profile": "adaptive", + "blake3": "fcb9b8d9517b3e76ae4f0a800222ca1624cc59d6a594f51781515d661bd169c2" + } + ] + } + }, + { + "file": "qasm/timing.qasm", + "format": "qasm", + "covers": [ + "timing each format leaves out, and says so" + ], + "expect": { + "outcomes": [ + "00", + "11" + ], + "writes": [ + { + "to": "qir", + "profile": "base", + "notes": [ + "Q721" + ] + }, + { + "to": "jeff", + "notes": [ + "Q721" + ] + } + ] + } + } + ] +} diff --git a/wai/quantum-conformance/formats/qasm/clifford_t.qasm b/wai/quantum-conformance/formats/qasm/clifford_t.qasm new file mode 100644 index 000000000..27be85d2b --- /dev/null +++ b/wai/quantum-conformance/formats/qasm/clifford_t.qasm @@ -0,0 +1,14 @@ +// A Clifford+T program: written to QIR in Clifford+T intrinsics alone, no +// rotation, and read back to the bit-exact method. +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +bit[3] c; +h q[0]; +t q[0]; +p(pi/4) q[1]; +p(3*pi/4) q[2]; +cx q[0], q[1]; +ccx q[0], q[1], q[2]; +s q[2]; +c = measure q; diff --git a/wai/quantum-conformance/formats/qasm/every_gate.qasm b/wai/quantum-conformance/formats/qasm/every_gate.qasm new file mode 100644 index 000000000..856372de9 --- /dev/null +++ b/wai/quantum-conformance/formats/qasm/every_gate.qasm @@ -0,0 +1,17 @@ +// Every gate of the language once, on qubits put in superpositions first, +// then every qubit measured: written to QIR and jeff, each read back the same +// circuit. +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +bit[3] c; +h q[0]; +ry(0.37) q[1]; +rx(1.1) q[2]; +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.3) q[0]; ry(-0.7) q[1]; rz(1.9) q[2]; p(0.45) q[0]; p(pi/4) q[1]; p(-pi/2) q[2]; U(0.2, 0.9, -1.4) q[1]; +cx q[0], q[1]; cy q[1], q[2]; cz q[2], q[0]; ch q[0], q[2]; cp(0.8) q[1], q[0]; +crx(0.6) q[0], q[1]; cry(-1.2) q[2], q[1]; crz(2.2) q[1], q[2]; cu(0.3, 0.4, 0.5, 0.6) q[0], q[2]; +swap q[0], q[2]; iswap q[1], q[0]; rxx(0.7) q[0], q[1]; ryy(1.1) q[1], q[2]; rzz(-0.4) q[2], q[0]; +ccx q[0], q[1], q[2]; ccz q[2], q[0], q[1]; cswap q[1], q[0], q[2]; +c = measure q; diff --git a/wai/quantum-conformance/formats/qasm/measure_reset.qasm b/wai/quantum-conformance/formats/qasm/measure_reset.qasm new file mode 100644 index 000000000..89a34d431 --- /dev/null +++ b/wai/quantum-conformance/formats/qasm/measure_reset.qasm @@ -0,0 +1,13 @@ +// A measurement in the middle and a reset, with no branch: written to jeff +// with measureNd and reset, and to QIR under the adaptive profile. +OPENQASM 3.0; +include "stdgates.inc"; +qubit[2] q; +bit[3] c; +h q[0]; +cx q[0], q[1]; +c[0] = measure q[0]; +reset q[0]; +x q[0]; +c[1] = measure q[0]; +c[2] = measure q[1]; diff --git a/wai/quantum-conformance/formats/qasm/teleport.qasm b/wai/quantum-conformance/formats/qasm/teleport.qasm new file mode 100644 index 000000000..98e1e443d --- /dev/null +++ b/wai/quantum-conformance/formats/qasm/teleport.qasm @@ -0,0 +1,20 @@ +// Teleportation of |1>, corrected on what was measured: written to QIR under +// the adaptive profile, every shot reads 1 on c[2]. jeff writes no branch. +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +bit[3] c; +x q[0]; +h q[1]; +cx q[1], q[2]; +cx q[0], q[1]; +h q[0]; +c[0] = measure q[0]; +c[1] = measure q[1]; +if (c[1]) { + x q[2]; +} +if (c[0] == 1) { + z q[2]; +} +c[2] = measure q[2]; diff --git a/wai/quantum-conformance/formats/qasm/timing.qasm b/wai/quantum-conformance/formats/qasm/timing.qasm new file mode 100644 index 000000000..0e8730725 --- /dev/null +++ b/wai/quantum-conformance/formats/qasm/timing.qasm @@ -0,0 +1,11 @@ +// A barrier and a delay: neither format has a place for them, so they are +// left out, and the conversion says so. +OPENQASM 3.0; +include "stdgates.inc"; +qubit[2] q; +bit[2] c; +h q[0]; +barrier q; +delay[20ns] q[1]; +cx q[0], q[1]; +c = measure q; diff --git a/wai/quantum-conformance/formats/qir/bell.ref.qasm b/wai/quantum-conformance/formats/qir/bell.ref.qasm new file mode 100644 index 000000000..0a46d8280 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/bell.ref.qasm @@ -0,0 +1,7 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[2] q; +bit[2] c; +h q[0]; +cx q[0], q[1]; +c = measure q; diff --git a/wai/quantum-conformance/formats/qir/bell_base.ll b/wai/quantum-conformance/formats/qir/bell_base.ll new file mode 100644 index 000000000..e7c842090 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/bell_base.ll @@ -0,0 +1,52 @@ +; The base profile's own example program (the QIR specification, MIT): two +; qubits entangled, measured, and a tuple of the two results recorded. + +@0 = internal constant [3 x i8] c"r1\00" +@1 = internal constant [3 x i8] c"r2\00" +@2 = internal constant [3 x i8] c"t0\00" + +define i64 @Entry_Point_Name() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body + +body: ; preds = %entry + call void @__quantum__qis__h__body(ptr null) + call void @__quantum__qis__cnot__body(ptr null, ptr inttoptr (i64 1 to ptr)) + br label %measurements + +measurements: ; preds = %body + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) + br label %output + +output: ; preds = %measurements + call void @__quantum__rt__tuple_record_output(i64 2, ptr @2) + call void @__quantum__rt__result_record_output(ptr null, ptr @0) + call void @__quantum__rt__result_record_output(ptr inttoptr (i64 1 to ptr), ptr @1) + + ret i64 0 +} + +declare void @__quantum__qis__h__body(ptr) + +declare void @__quantum__qis__cnot__body(ptr, ptr) + +declare void @__quantum__qis__mz__body(ptr, ptr writeonly) #1 + +declare void @__quantum__rt__initialize(ptr) + +declare void @__quantum__rt__tuple_record_output(i64, ptr) + +declare void @__quantum__rt__result_record_output(ptr, ptr) + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="schema_id" "required_num_qubits"="2" "required_num_results"="2" } + +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/clifford_t.ll b/wai/quantum-conformance/formats/qir/clifford_t.ll new file mode 100644 index 000000000..4324e73c3 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/clifford_t.ll @@ -0,0 +1,30 @@ +; A Clifford+T module, one rotation by exactly pi/4 in hex: it reads with no note and runs on the bit-exact method. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + call void @__quantum__qis__t__body(ptr null) + call void @__quantum__qis__cx__body(ptr null, ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__s__adj(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, ptr inttoptr (i64 1 to ptr)) + br label %measurements +measurements: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1 + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) #1 + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="2" "required_num_results"="2" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/clifford_t.ref.qasm b/wai/quantum-conformance/formats/qir/clifford_t.ref.qasm new file mode 100644 index 000000000..a7cf7acc6 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/clifford_t.ref.qasm @@ -0,0 +1,11 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[2] q; +bit[2] c; +h q[0]; +t q[0]; +cx q[0], q[1]; +sdg q[1]; +h q[1]; +rz(pi/4) q[1]; +c = measure q; diff --git a/wai/quantum-conformance/formats/qir/computed.ll b/wai/quantum-conformance/formats/qir/computed.ll new file mode 100644 index 000000000..31105a747 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/computed.ll @@ -0,0 +1,35 @@ +; Adaptive: an angle computed from constants under float_computations, and a branch on two results combined by i1 logic: result 2 is 1 exactly when results 0 and 1 agree. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + %a = fadd double 0.25, 0.5 + %b = fmul double %a, 2.0 + call void @__quantum__qis__ry__body(double %b, ptr null) + call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) + %r0 = call i1 @__quantum__rt__read_result(ptr readonly null) + %r1 = call i1 @__quantum__rt__read_result(ptr readonly inttoptr (i64 1 to ptr)) + %x = xor i1 %r0, %r1 + %n = xor i1 %x, true + br i1 %n, label %same, label %out +same: + call void @__quantum__qis__x__body(ptr inttoptr (i64 2 to ptr)) + br label %out +out: + call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr)) + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="3" "required_num_results"="3" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +!4 = !{i32 5, !"float_computations", !10} +!5 = !{i32 1, !"backwards_branching", i2 0} +!10 = !{!"double"} diff --git a/wai/quantum-conformance/formats/qir/every_intrinsic.ll b/wai/quantum-conformance/formats/qir/every_intrinsic.ll new file mode 100644 index 000000000..2f979b413 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/every_intrinsic.ll @@ -0,0 +1,51 @@ +; Every intrinsic the reader takes, once each, at angles that are not dyadic where an intrinsic takes one (decimal, exponent and hex), then every qubit measured. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__h__body(ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__x__body(ptr null) + call void @__quantum__qis__y__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__z__body(ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__s__body(ptr null) + call void @__quantum__qis__s__adj(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__t__body(ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__t__adj(ptr null) + call void @__quantum__qis__sx__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__rx__body(double 3.000000e-01, ptr null) + call void @__quantum__qis__ry__body(double 0xBFF199999999999A, ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__rz__body(double 2.5, ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__rz__adj(double 0.7, ptr null) + call void @__quantum__qis__r1__body(double 0.9, ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__cnot__body(ptr null, ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__cx__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__cy__body(ptr inttoptr (i64 2 to ptr), ptr null) + call void @__quantum__qis__cz__body(ptr null, ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__swap__body(ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__ccx__body(ptr null, ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__ccz__body(ptr inttoptr (i64 2 to ptr), ptr inttoptr (i64 1 to ptr), ptr null) + call void @__quantum__qis__rxx__body(double 0.4, ptr null, ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__ryy__body(double -0.6, ptr inttoptr (i64 1 to ptr), ptr inttoptr (i64 2 to ptr)) + call void @__quantum__qis__rzz__body(double 1.3, ptr inttoptr (i64 2 to ptr), ptr null) + br label %measurements +measurements: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1 + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) #1 + call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr)) #1 + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="3" "required_num_results"="3" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/every_intrinsic.ref.qasm b/wai/quantum-conformance/formats/qir/every_intrinsic.ref.qasm new file mode 100644 index 000000000..b21a91cba --- /dev/null +++ b/wai/quantum-conformance/formats/qir/every_intrinsic.ref.qasm @@ -0,0 +1,10 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +bit[3] c; +h q[0]; h q[1]; h q[2]; +x q[0]; y q[1]; z q[2]; s q[0]; sdg q[1]; t q[2]; tdg q[0]; sx q[1]; +rx(0.3) q[0]; ry(-1.1) q[1]; rz(2.5) q[2]; rz(-0.7) q[0]; p(0.9) q[1]; +cx q[0], q[1]; cx q[1], q[2]; cy q[2], q[0]; cz q[0], q[2]; swap q[1], q[2]; ccx q[0], q[1], q[2]; ctrl(2) @ z q[2], q[1], q[0]; +rxx(0.4) q[0], q[1]; ryy(-0.6) q[1], q[2]; rzz(1.3) q[2], q[0]; +c = measure q; diff --git a/wai/quantum-conformance/formats/qir/no_profile.ll b/wai/quantum-conformance/formats/qir/no_profile.ll new file mode 100644 index 000000000..865767611 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/no_profile.ll @@ -0,0 +1,24 @@ +; A module that names no profile: read under the profile its instructions need (base), and said so. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + br label %measurements +measurements: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1 + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/qft3_typed.ll b/wai/quantum-conformance/formats/qir/qft3_typed.ll new file mode 100644 index 000000000..350c8340e --- /dev/null +++ b/wai/quantum-conformance/formats/qir/qft3_typed.ll @@ -0,0 +1,51 @@ +; QIR emitted by wai-quantum — deterministic, byte-identical on every target. +source_filename = "wai_quantum" + +%Qubit = type opaque +%Result = type opaque + +declare void @__quantum__qis__h__body(%Qubit*) +declare void @__quantum__qis__rz__body(double, %Qubit*) +declare void @__quantum__qis__cnot__body(%Qubit*, %Qubit*) +declare void @__quantum__qis__mz__body(%Qubit*, %Result*) +declare void @__quantum__rt__result_record_output(%Result*, i8*) + +define void @main() #0 { +entry: + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0xBFE921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FD921FB54442D18, %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0xBFD921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FD921FB54442D18, %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0xBFE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__rz__body(double 0x3FE921FB54442D18, %Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) + call void @__quantum__qis__cnot__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*)) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* null) + ret void +} + +attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="3" "required_num_results"="3" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 1} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/qft3_typed.ref.qasm b/wai/quantum-conformance/formats/qir/qft3_typed.ref.qasm new file mode 100644 index 000000000..fbb19a005 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/qft3_typed.ref.qasm @@ -0,0 +1,12 @@ +OPENQASM 3.0; +include "stdgates.inc"; +qubit[3] q; +h q[0]; +cp(2*pi/4) q[1],q[0]; +cp(2*pi/8) q[2],q[0]; +h q[1]; +cp(2*pi/4) q[2],q[1]; +h q[2]; +cx q[0],q[2]; +cx q[2],q[0]; +cx q[0],q[2]; diff --git a/wai/quantum-conformance/formats/qir/refuse/base_branch.ll b/wai/quantum-conformance/formats/qir/refuse/base_branch.ll new file mode 100644 index 000000000..6ba97f8dc --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/base_branch.ll @@ -0,0 +1,25 @@ +; Under the base profile, reading a result: forbidden. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + br label %measurements +measurements: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1 + %r = call i1 @__quantum__rt__read_result(ptr readonly null) + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/base_gate_after_measure.ll b/wai/quantum-conformance/formats/qir/refuse/base_gate_after_measure.ll new file mode 100644 index 000000000..6b20fc7ba --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/base_gate_after_measure.ll @@ -0,0 +1,24 @@ +; Under the base profile, a gate on a qubit after its measurement: forbidden. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) #1 + br label %measurements +measurements: + call void @__quantum__qis__x__body(ptr null) + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/dynamic_qubits.ll b/wai/quantum-conformance/formats/qir/refuse/dynamic_qubits.ll new file mode 100644 index 000000000..baa4b1698 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/dynamic_qubits.ll @@ -0,0 +1,18 @@ +; A qubit allocated at run time, with dynamic qubit management declared: a capability this reader does not take. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + %q = call ptr @__quantum__rt__qubit_allocate() + call void @__quantum__qis__h__body(ptr %q) + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="0" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 true} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/loop_declared.ll b/wai/quantum-conformance/formats/qir/refuse/loop_declared.ll new file mode 100644 index 000000000..08c2627bf --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/loop_declared.ll @@ -0,0 +1,23 @@ +; The same loop with backwards_branching declared: a capability this reader does not take. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %l +l: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + %r = call i1 @__quantum__rt__read_result(ptr readonly null) + br i1 %r, label %l, label %done +done: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +!4 = !{i32 1, !"backwards_branching", i2 2} diff --git a/wai/quantum-conformance/formats/qir/refuse/loop_undeclared.ll b/wai/quantum-conformance/formats/qir/refuse/loop_undeclared.ll new file mode 100644 index 000000000..e15e48d18 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/loop_undeclared.ll @@ -0,0 +1,22 @@ +; A loop (a branch back to an earlier block) with no backwards_branching flag: forbidden. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %l +l: + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + %r = call i1 @__quantum__rt__read_result(ptr readonly null) + br i1 %r, label %l, label %done +done: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/major_version.ll b/wai/quantum-conformance/formats/qir/refuse/major_version.ll new file mode 100644 index 000000000..df3a0d118 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/major_version.ll @@ -0,0 +1,24 @@ +; A QIR major version the reader does not take. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + br label %measurements +measurements: + + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="0" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 3} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/not_llvm.ll b/wai/quantum-conformance/formats/qir/refuse/not_llvm.ll new file mode 100644 index 000000000..32e441bca --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/not_llvm.ll @@ -0,0 +1,3 @@ +; Not LLVM text: no function, no entry point. +OPENQASM 3.0; +qubit q; diff --git a/wai/quantum-conformance/formats/qir/refuse/qubit_past_count.ll b/wai/quantum-conformance/formats/qir/refuse/qubit_past_count.ll new file mode 100644 index 000000000..0cb8fe7b0 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/qubit_past_count.ll @@ -0,0 +1,24 @@ +; Qubit 4 in a module that declares one qubit. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr inttoptr (i64 4 to ptr)) + br label %measurements +measurements: + + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="0" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/stale_read.ll b/wai/quantum-conformance/formats/qir/refuse/stale_read.ll new file mode 100644 index 000000000..1de2fb66e --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/stale_read.ll @@ -0,0 +1,24 @@ +; A result read, measured again into the same result, and only then branched on: the branch would read the later measurement. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + %r = call i1 @__quantum__rt__read_result(ptr readonly null) + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly null) + br i1 %r, label %a, label %b +a: + call void @__quantum__qis__x__body(ptr inttoptr (i64 1 to ptr)) + br label %b +b: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="2" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/switch_declared.ll b/wai/quantum-conformance/formats/qir/refuse/switch_declared.ll new file mode 100644 index 000000000..0437f6284 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/switch_declared.ll @@ -0,0 +1,24 @@ +; A switch on a result, multiple-target branching declared: a capability this reader does not take. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + %r = call i1 @__quantum__rt__read_result(ptr readonly null) + switch i1 %r, label %done [ i1 true, label %one ] +one: + call void @__quantum__qis__x__body(ptr null) + br label %done +done: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="1" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +!4 = !{i32 1, !"multiple_target_branching", i1 true} diff --git a/wai/quantum-conformance/formats/qir/refuse/undeclared_float.ll b/wai/quantum-conformance/formats/qir/refuse/undeclared_float.ll new file mode 100644 index 000000000..0131a4d63 --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/undeclared_float.ll @@ -0,0 +1,32 @@ +; The same computation with no float_computations flag: what the adaptive profile forbids, refused on its line. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + %a = fadd double 0.25, 0.5 + %b = fmul double %a, 2.0 + call void @__quantum__qis__ry__body(double %b, ptr null) + call void @__quantum__qis__h__body(ptr inttoptr (i64 1 to ptr)) + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) + %r0 = call i1 @__quantum__rt__read_result(ptr readonly null) + %r1 = call i1 @__quantum__rt__read_result(ptr readonly inttoptr (i64 1 to ptr)) + %x = xor i1 %r0, %r1 + %n = xor i1 %x, true + br i1 %n, label %same, label %out +same: + call void @__quantum__qis__x__body(ptr inttoptr (i64 2 to ptr)) + br label %out +out: + call void @__quantum__qis__mz__body(ptr inttoptr (i64 2 to ptr), ptr writeonly inttoptr (i64 2 to ptr)) + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="labels" "required_num_qubits"="3" "required_num_results"="3" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/unknown_intrinsic.ll b/wai/quantum-conformance/formats/qir/refuse/unknown_intrinsic.ll new file mode 100644 index 000000000..db7becd4b --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/unknown_intrinsic.ll @@ -0,0 +1,24 @@ +; An intrinsic the reader does not know. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__foo__body(ptr null) + br label %measurements +measurements: + + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="0" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/refuse/unknown_profile.ll b/wai/quantum-conformance/formats/qir/refuse/unknown_profile.ll new file mode 100644 index 000000000..66eb7df8b --- /dev/null +++ b/wai/quantum-conformance/formats/qir/refuse/unknown_profile.ll @@ -0,0 +1,24 @@ +; A profile the reader does not take. + +define i64 @main() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body +body: + call void @__quantum__qis__h__body(ptr null) + br label %measurements +measurements: + + br label %output +output: + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="custom_profile" "output_labeling_schema"="labels" "required_num_qubits"="1" "required_num_results"="0" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 1} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} diff --git a/wai/quantum-conformance/formats/qir/teleport_chain.ll b/wai/quantum-conformance/formats/qir/teleport_chain.ll new file mode 100644 index 000000000..c28fdf49a --- /dev/null +++ b/wai/quantum-conformance/formats/qir/teleport_chain.ll @@ -0,0 +1,121 @@ +; The adaptive profile's own example program (the QIR specification, MIT): a +; Bell pair whose second half is teleported twice, each step measuring in +; the middle and correcting on the result. The pair ends between qubits 0 and +; 5, so results 4 and 5 are equal on every shot. + +@0 = internal constant [5 x i8] c"0_t0\00" +@1 = internal constant [5 x i8] c"0_t1\00" + +define i64 @TeleportChain() local_unnamed_addr #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body + +body: ; preds = %entry + tail call void @__quantum__qis__h__body(ptr null) + tail call void @__quantum__qis__cnot__body(ptr null, ptr nonnull inttoptr (i64 1 to ptr)) + tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 2 to ptr)) + tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 2 to ptr), ptr nonnull inttoptr (i64 4 to ptr)) + tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 3 to ptr)) + tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 3 to ptr), ptr nonnull inttoptr (i64 5 to ptr)) + tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 1 to ptr), ptr nonnull inttoptr (i64 2 to ptr)) + tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 1 to ptr)) + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 1 to ptr), ptr writeonly null) + tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 1 to ptr)) + %0 = tail call i1 @__quantum__rt__read_result(ptr readonly null) + br i1 %0, label %then__1, label %continue__1 + +; conditional quantum gate (only one in this block, but many can appear and the full quantum instruction set should be usable) +then__1: ; preds = %body + tail call void @__quantum__qis__z__body(ptr nonnull inttoptr (i64 4 to ptr)) + br label %continue__1 + +continue__1: ; preds = %then__1, %body + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 2 to ptr), ptr writeonly nonnull inttoptr (i64 1 to ptr)) + tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 2 to ptr)) + %1 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 1 to ptr)) + br i1 %1, label %then__2, label %continue__2 + +then__2: ; preds = %continue__1 + tail call void @__quantum__qis__x__body(ptr nonnull inttoptr (i64 4 to ptr)) + br label %continue__2 + +continue__2: ; preds = %then__2, %continue__1 + tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 4 to ptr), ptr nonnull inttoptr (i64 3 to ptr)) + tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 4 to ptr)) + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 4 to ptr), ptr writeonly nonnull inttoptr (i64 2 to ptr)) + tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 4 to ptr)) + %2 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 2 to ptr)) + br i1 %2, label %then__3, label %continue__3 + +then__3: ; preds = %continue__2 + tail call void @__quantum__qis__z__body(ptr nonnull inttoptr (i64 5 to ptr)) + br label %continue__3 + +continue__3: ; preds = %then__3, %continue__2 + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 3 to ptr), ptr writeonly nonnull inttoptr (i64 3 to ptr)) + tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 3 to ptr)) + %3 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 3 to ptr)) + br i1 %3, label %then__4, label %continue__4 + +then__4: ; preds = %continue__3 + tail call void @__quantum__qis__x__body(ptr nonnull inttoptr (i64 5 to ptr)) + br label %continue__4 + +continue__4: ; preds = %continue__3, %then__4 + tail call void @__quantum__qis__mz__body(ptr null, ptr writeonly nonnull inttoptr (i64 4 to ptr)) + tail call void @__quantum__qis__reset__body(ptr null) + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 5 to ptr), ptr writeonly nonnull inttoptr (i64 5 to ptr)) + tail call void @__quantum__qis__reset__body(ptr nonnull inttoptr (i64 5 to ptr)) + br label %exit + +exit: + call void @__quantum__rt__result_record_output(ptr nonnull inttoptr (i64 4 to ptr), ptr @0) + call void @__quantum__rt__result_record_output(ptr nonnull inttoptr (i64 5 to ptr), ptr @1) + ret i64 0 +} + +; declarations of QIS functions + +declare void @__quantum__qis__cnot__body(ptr, ptr) local_unnamed_addr + +declare void @__quantum__qis__h__body(ptr) local_unnamed_addr + +declare void @__quantum__qis__x__body(ptr) local_unnamed_addr + +declare void @__quantum__qis__z__body(ptr) local_unnamed_addr + +declare void @__quantum__qis__reset__body(ptr) local_unnamed_addr + +declare void @__quantum__qis__mz__body(ptr, ptr writeonly) #1 + +; declarations of runtime functions + +declare void @__quantum__rt__initialize(ptr) + +declare i1 @__quantum__rt__read_result(ptr readonly) + +declare void @__quantum__rt__result_record_output(ptr, ptr) + +; attributes + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="schema_id" "required_num_qubits"="6" "required_num_results"="6" } + +attributes #1 = { "irreversible" } + +; module flags + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9} + +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +!4 = !{i32 5, !"int_computations", !10} +!5 = !{i32 5, !"float_computations", !11} +!6 = !{i32 1, !"ir_functions", i1 false} +!7 = !{i32 1, !"backwards_branching", i2 0} +!8 = !{i32 1, !"multiple_target_branching", i1 false} +!9 = !{i32 1, !"multiple_return_points", i1 false} +!10 = !{!"i32", !"i64"} +!11 = !{!"float", !"double"} diff --git a/wai/quantum-studio/README.md b/wai/quantum-studio/README.md index 639e34e01..6bcbbcba2 100644 --- a/wai/quantum-studio/README.md +++ b/wai/quantum-studio/README.md @@ -65,6 +65,8 @@ The engine makes each edit in the program's own text, through its syntax tree, s The diagram's description, which a screen reader reads, is the engine's account of the circuit: its qubits and bits, then each column, each gate with its role on each qubit, each measurement with its bit, and each condition as the legend writes it. "SVG" saves the drawing as the engine writes it, a standalone image that follows the colour of the text it is placed in, and "LaTeX" saves it as one picture in plain TikZ that needs only `\usepackage{tikz}`. +"Open" takes a program in OpenQASM, or in QIR (`.ll`, `.qir`) or jeff (`.jeff`), which the engine reads to OpenQASM 3 in the page (`engine_convert`; [the formats page](../wai-quantum/docs/formats.md)): the program goes into the editor, and the line under it names the format and its version, the check (the program written back to its format and read again) and each of the reader's notes with its code. A module the reader refuses leaves the editor as it was, and the status line gives the refusal with its code and its place. + ## Device Under **Device**, a device target: one of the built-in targets (synthetic, in the shapes of published formats), one imported here (**Import…** reads a calibration or target snapshot in any format the engine reads, kept in this browser), or, in the desktop app, one kept with `wai-quantum targets import`. The coupling map is drawn with each pair coloured by its two-qubit error and each qubit by its readout error, one-qubit error or T1. The colours are five bins of one hue, by quantile of the device's own values; the poorest pairs are drawn thicker, every mark gives its numbers on hover, and **As a table** lists them. With a device chosen, a run is compiled to its gates, placed on the qubits and routes most likely to succeed, and simulated under its noise; the noise fields wait, since the noise is the device's own. The layout the plan chose is drawn on the map before the run. The result's **Device** block names the target and its hash (in the receipt), the placement and its expected success beside the fewest swaps', the noise, and how far the counts are from the ideal ones. It is a model of the device, and it says so. On the service, a built-in target goes by name and any other as its document. diff --git a/wai/quantum-studio/README.zh-CN.md b/wai/quantum-studio/README.zh-CN.md index 6eb4b51e3..50f877fb6 100644 --- a/wai/quantum-studio/README.zh-CN.md +++ b/wai/quantum-studio/README.zh-CN.md @@ -69,6 +69,8 @@ Ctrl+Enter(macOS 上为 ⌘+Enter)运行程序,Escape 取消运行。程 线路图的描述(供屏幕阅读器朗读)是引擎对线路的叙述:先是其量子比特和比特,然后是每一列,每个门及其在每个量子比特上的角色,每次测量及其比特,以及按图例写法给出的每个条件。“SVG”按引擎写出的样子保存线路图,这是一张独立的图像,颜色随其所在位置的文字颜色而变;“LaTeX”则把它保存为一幅纯 TikZ 图,只需要 `\usepackage{tikz}`。 +“打开”(Open)接受 OpenQASM 程序,也接受 QIR(`.ll`、`.qir`)或 jeff(`.jeff`)文件,由引擎在页面中读取为 OpenQASM 3(`engine_convert`;[格式说明页](../wai-quantum/docs/formats.md)):程序放入编辑器,其下的一行说明格式及其版本、检查结果(程序写回原格式后再读取一次)以及读取器的每条说明及其代码。读取器拒绝的模块不会改变编辑器中的内容,状态行给出拒绝原因及其代码和位置。 + ## 设备 在**设备**(Device)下选择一个设备目标:内置目标之一(合成的,采用已公开格式的形态)、在此导入的目标(**导入…**(Import…)读取引擎能读取的任何格式的校准快照或目标快照,保存在这个浏览器中),或者在桌面应用中,用 `wai-quantum targets import` 保存的目标。耦合图中,每个耦合对按其双量子比特门误差着色,每个量子比特按其读出误差、单量子比特门误差或 T1 着色。颜色为同一色调的五档,按该设备自身数值的分位数划分;最差的耦合对画得更粗,每个标记在悬停时给出其数值,**以表格显示**(As a table)则把它们列出。选定设备后,运行会被编译为该设备的门,放置到最可能成功的量子比特和路由上,并在其噪声下模拟;噪声字段暂不起作用,因为噪声取自设备本身。计划选定的布局会在运行之前画在耦合图上。结果中的**设备**区块列出目标及其哈希(写入回执)、放置方式及其预期成功率(与交换次数最少的放置方式的预期成功率并列)、噪声,以及计数与理想计数相差多少。这是设备的一个模型,结果也如此注明。在作业服务上,内置目标按名称传递,其他目标则以其文档传递。 diff --git a/wai/quantum-studio/locales/en/frame.json b/wai/quantum-studio/locales/en/frame.json index e1c9666bc..4840d68d6 100644 --- a/wai/quantum-studio/locales/en/frame.json +++ b/wai/quantum-studio/locales/en/frame.json @@ -151,6 +151,9 @@ "frame.fixed": "Fixed, as {version}. Undo brings the lines back.", "frame.saved_job": "Saved {name}. wai-quantum verify checks it.", "frame.opened": "Opened {name}.", + "frame.opened_from": "Opened {name}, read from {format} as OpenQASM 3 (written back and checked: {check}). Notes: {notes}.", + "frame.open_refused": "{name} does not read as {format}: {error}", + "frame.no_notes": "none", "frame.copied": "Copied a link with the program in its fragment, which no server sees.", "frame.from_history": "From the history: run on {where}, {when}.", "frame.history_empty": "Jobs you run appear here, kept on this device.", diff --git a/wai/quantum-studio/locales/zh-CN/frame.json b/wai/quantum-studio/locales/zh-CN/frame.json index 3517dfb0e..4608b940b 100644 --- a/wai/quantum-studio/locales/zh-CN/frame.json +++ b/wai/quantum-studio/locales/zh-CN/frame.json @@ -151,6 +151,9 @@ "frame.fixed": "已修正,记为 {version}。撤销即可恢复这些行。", "frame.saved_job": "已保存 {name}。可用 wai-quantum verify 检查它。", "frame.opened": "已打开 {name}。", + "frame.opened_from": "已打开 {name},从 {format} 读取为 OpenQASM 3(写回后检查:{check})。说明:{notes}。", + "frame.open_refused": "{name} 无法按 {format} 读取:{error}", + "frame.no_notes": "无", "frame.copied": "已复制一个把程序放在片段中的链接,任何服务器都看不到它。", "frame.from_history": "来自历史:在{where}上运行,{when}。", "frame.history_empty": "你运行的作业会出现在这里,保存在这台设备上。", diff --git a/wai/quantum-studio/src/engine.js b/wai/quantum-studio/src/engine.js index 3c2ced4c4..ca1e18acd 100644 --- a/wai/quantum-studio/src/engine.js +++ b/wai/quantum-studio/src/engine.js @@ -64,6 +64,9 @@ export class TabEngine { verify(job) { return this.call('verify', { job }); } critique(qasm) { return this.call('critique', { qasm }); } equivalent(a, b) { return this.call('equivalent', { a, b }); } + // A program carried between formats (engine::formats): {to, program | program_base64, from?} in, the + // conversion (or its refusal as data, {error, code}) out. + convert(request) { return this.call('convert', { request }); } variants(qasm) { return this.call('variants', { qasm }); } shortcuts(qasm, thorough = false) { return this.call('shortcuts', { qasm, thorough }); } // A compile ({qasm, epsilon, level, basis, stages}): sealed with this browser's key. @@ -198,6 +201,8 @@ export class DesktopEngine { // A critique names a problem as {ok: false, error}, as a lint does. async critique(qasm) { return JSON.parse(await this.invoke('critique', { qasm })); } equivalent(a, b) { return this.call('equivalent', { a, b }); } + // A refusal comes back as data ({error, code}), as the tab's does. + async convert(request) { return JSON.parse(await this.invoke('convert', { request: JSON.stringify(request) })); } variants(qasm) { return this.call('variants', { qasm }); } shortcuts(qasm, thorough = false) { return this.call('shortcuts', { qasm, thorough }); } // A compile, sealed with this user's key. diff --git a/wai/quantum-studio/src/main.js b/wai/quantum-studio/src/main.js index d0ef918ca..4bebd3b07 100644 --- a/wai/quantum-studio/src/main.js +++ b/wai/quantum-studio/src/main.js @@ -110,6 +110,13 @@ const handsField = StateField.define({ }); const handGutter = gutter({ class: 'qs-hand-gutter', markers: (v) => v.state.field(handsField), initialSpacer: () => new HandMark('spacer', '') }); +// Bytes as standard base64, for a binary program sent to the engine. +function toBase64(bytes) { + let s = ''; + for (let i = 0; i < bytes.length; i += 0x8000) s += String.fromCharCode(...bytes.subarray(i, i + 0x8000)); + return btoa(s); +} + function mount(root) { const desktop = onDesktop(); // A host page with a heading of its own mounts the Studio with data-embedded, and its name is not a second h1. @@ -195,7 +202,7 @@ function mount(root) { // ---- the program ---- const lintBadge = el('span', { class: 'qs-badge', id: 'qs-lint', text: '…' }); - const fileIn = el('input', { type: 'file', accept: '.qasm,.txt,text/plain', hidden: true, 'aria-hidden': 'true' }); + const fileIn = el('input', { type: 'file', id: 'qs-open-file', accept: '.qasm,.ll,.qir,.jeff,.txt,text/plain', hidden: true, 'aria-hidden': 'true' }); const shows = el('p', { class: 'qs-shows', id: 'qs-shows' }); const reviewBox = el('div', { class: 'qs-review', id: 'qs-review', hidden: true, role: 'region', 'aria-label': tr('frame.review_label') }); const editorHost = el('div', { class: 'qs-editor', id: 'qs-editor' }); @@ -823,9 +830,25 @@ function mount(root) { openBtn.addEventListener('click', () => fileIn.click()); fileIn.addEventListener('change', async () => { const f = fileIn.files?.[0]; if (!f) return; - const body = await f.text(); - if (await from(`the file ${f.name}`, () => setText(body))) shows.textContent = tr('frame.opened', { name: f.name }); fileIn.value = ''; + // QIR and jeff are read by the engine to OpenQASM 3, with what it noted. + const format = /\.(ll|qir)$/i.test(f.name) ? 'qir' : /\.jeff$/i.test(f.name) ? 'jeff' : null; + if (!format) { + const body = await f.text(); + if (await from(`the file ${f.name}`, () => setText(body))) shows.textContent = tr('frame.opened', { name: f.name }); + return; + } + let r; + try { + const request = { from: format, to: 'qasm' }; + if (format === 'jeff') request.program_base64 = toBase64(new Uint8Array(await f.arrayBuffer())); + else request.program = await f.text(); + r = await local.convert(request); + } catch (e) { r = { error: e.message }; } + if (r.error) { setStatus(tr('frame.open_refused', { name: f.name, format: format === 'qir' ? 'QIR' : 'jeff', error: r.error }), 'bad'); return; } + if (!(await from(`the file ${f.name}`, () => setText(r.program)))) return; + const notes = (r.notes || []).map((n) => `${n.message} [${n.code}]`); + shows.textContent = tr('frame.opened_from', { name: f.name, format: format === 'qir' ? `QIR ${r.read?.version ?? ''}`.trim() : `jeff ${r.read?.version ?? ''}`.trim(), check: r.check?.verdict?.verdict ?? 'unchecked', notes: notes.length ? notes.join('; ') : tr('frame.no_notes') }); }); shareBtn.addEventListener('click', () => { const u = new URL(location.href); u.hash = 'qasm=' + encodeURIComponent(text()); u.search = ''; diff --git a/wai/quantum-studio/src/worker.js b/wai/quantum-studio/src/worker.js index 622363cf4..fba6efab8 100644 --- a/wai/quantum-studio/src/worker.js +++ b/wai/quantum-studio/src/worker.js @@ -39,6 +39,7 @@ self.onmessage = async (ev) => { case 'verify': text = mod.engine_verify(JSON.stringify(ev.data.job)); break; case 'critique': text = mod.engine_critique(ev.data.qasm); break; case 'equivalent': text = mod.engine_equivalent(ev.data.a, ev.data.b); break; + case 'convert': text = mod.engine_convert(JSON.stringify(ev.data.request)); break; case 'variants': text = mod.engine_variants(ev.data.qasm); break; case 'shortcuts': text = mod.engine_shortcuts(ev.data.qasm, !!ev.data.thorough); break; case 'compile': text = e().compile(JSON.stringify(ev.data.request)); break; diff --git a/wai/quantum-studio/test/studio.test.mjs b/wai/quantum-studio/test/studio.test.mjs index 04f7ab5d8..e2815250d 100644 --- a/wai/quantum-studio/test/studio.test.mjs +++ b/wai/quantum-studio/test/studio.test.mjs @@ -12,6 +12,8 @@ // - "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 QIR module and a jeff module open as OpenQASM 3 through the engine, with its +// notes and check, and a module its profile forbids is refused with its code; // - a rotation runs on the state vector, ⟨Z0⟩ = cos 0.3; // - a device target: its coupling map with each pair and qubit coloured by its error, // a run placed on it and simulated under its noise with the target's hash in the @@ -212,6 +214,25 @@ await page.keyboard.press('ControlOrMeta+End'); for (let i = 0; i < 10; i++) await page.keyboard.press('Backspace'); await page.waitForFunction(() => /qubits/.test(document.querySelector('#qs-lint')?.textContent ?? ''), null, { timeout: LONG }); +// A program in another format: QIR and jeff opened, read by the engine to +// OpenQASM 3 with its notes and its check; a module the reader refuses keeps +// the editor as it was and says why, with its code. +{ + const formats = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', 'quantum-conformance', 'formats'); + await page.setInputFiles('#qs-open-file', { name: 'bell.ll', mimeType: 'text/plain', buffer: readFileSync(join(formats, 'qir', 'bell_base.ll')) }); + await page.waitForFunction(() => /read from QIR/.test(document.querySelector('#qs-shows')?.textContent ?? ''), null, { timeout: LONG }); + const fromQir = await page.evaluate(() => window.quantumStudio.view.state.doc.toString()); + check(/^\/\/ Read from QIR 2\.0, base profile/m.test(fromQir) && /OPENQASM 3\.0;/.test(fromQir) && /cx q\[0\], q\[1\];/.test(fromQir) && /checked: equivalent/.test(await page.textContent('#qs-shows')), `a QIR module opens as OpenQASM 3, checked (${(await page.textContent('#qs-shows')).slice(0, 120)}…)`); + await page.setInputFiles('#qs-open-file', { name: 'ghz.jeff', mimeType: 'application/octet-stream', buffer: readFileSync(join(formats, 'jeff', 'upstream', 'ghz-linear_3.jeff')) }); + await page.waitForFunction(() => /read from jeff/.test(document.querySelector('#qs-shows')?.textContent ?? ''), null, { timeout: LONG }); + const fromJeff = await page.evaluate(() => window.quantumStudio.view.state.doc.toString()); + check(/Read from jeff 0\.3/.test(fromJeff) && /qubit\[3\] q;/.test(fromJeff) && /\[Q715\]/.test(await page.textContent('#qs-shows')), `a jeff module opens as OpenQASM 3 with the reader's notes (${(await page.textContent('#qs-shows')).slice(0, 160)}…)`); + await page.waitForFunction(() => /qubits/.test(document.querySelector('#qs-lint')?.textContent ?? ''), null, { timeout: LONG }); + await page.setInputFiles('#qs-open-file', { name: 'forbidden.ll', mimeType: 'text/plain', buffer: readFileSync(join(formats, 'qir', 'refuse', 'base_branch.ll')) }); + await page.waitForFunction(() => /does not read as QIR/.test(document.querySelector('.qs-status')?.textContent ?? ''), null, { timeout: LONG }); + check(/\[Q704\]/.test(await page.textContent('.qs-status')) && (await page.evaluate(() => window.quantumStudio.view.state.doc.toString())) === fromJeff, `a module its profile forbids is refused with its code, the editor kept (${await page.textContent('.qs-status')})`); +} + // A rotation: any angle, on the state vector. await page.selectOption('#qs-example', 'rotation'); await page.waitForFunction(() => /State vector/.test(document.querySelector('#qs-plan')?.textContent ?? ''), null, { timeout: LONG }); diff --git a/wai/tools/quantum-docs/docs.json b/wai/tools/quantum-docs/docs.json index f604c6f5d..c0ca55d7c 100644 --- a/wai/tools/quantum-docs/docs.json +++ b/wai/tools/quantum-docs/docs.json @@ -1,6 +1,6 @@ { "schema": "wai.quantum.docs-manifest/1", - "about": "The documents check_docs.py runs: the WAI Quantum READMEs and docs pages (engine, command line, service, Python, Studio, desktop, editors, providers, the compiler, estimates, verdicts, error correction, the feature matrix and the version policy; the service's self-hosting, scheduler and provenance pages; the views' engine), the agent skill, the course's lessons, and their translations.", + "about": "The documents check_docs.py runs: the WAI Quantum READMEs and docs pages (engine, command line, service, Python, Studio, desktop, editors, providers, the compiler, estimates, verdicts, error correction, other formats, the feature matrix and the version policy; the service's self-hosting, scheduler and provenance pages; the views' engine), the agent skill, the course's lessons, and their translations.", "docs": [ "wai/wai-quantum/README.md", "wai/wai-quantum/README.zh-CN.md", @@ -10,6 +10,7 @@ "wai/wai-quantum/docs/estimate.md", "wai/wai-quantum/docs/verdicts.md", "wai/wai-quantum/docs/job-refusals.md", + "wai/wai-quantum/docs/formats.md", "wai/wai-quantum/docs/qasm-diagnostics.md", "wai/wai-quantum/docs/qasm-feature-matrix.md", "wai/wai-quantum/docs/qec.md", diff --git a/wai/wai-quantum-cli/README.md b/wai/wai-quantum-cli/README.md index 2c571345b..61baf34d9 100644 --- a/wai/wai-quantum-cli/README.md +++ b/wai/wai-quantum-cli/README.md @@ -222,6 +222,18 @@ The critique's figures are computed, not estimated: gates by kind, two-qubit gat A cheap route proves a design classically easy. None being cheap proves nothing about hardness: other methods, approximate ones above all, may cost far less. +## Other formats + +```sh runnable +wai-quantum convert prog.qasm --to qir > prog.ll # QIR 2.0, the base profile (adaptive for a program that branches) +wai-quantum convert prog.ll --to jeff -o prog.jeff # jeff; the format read is found from the file +wai-quantum convert prog.jeff --to qasm # back to OpenQASM 3 +wai-quantum run prog.ll --shots 100 # every verb reads QIR (.ll, .qir) and jeff (.jeff) +wai-quantum equivalent prog.jeff prog.qasm # exit 0: the same circuit +``` + +`convert` carries a program between OpenQASM 3, QIR (LLVM text) and jeff, and checks every conversion: what it writes is read back and held to the program by the equivalence check, and `check:` on standard error says how (`--no-check` skips it; `--json` gives everything as data, jeff as base64). QIR is read under the profile its entry point names, base or adaptive, and what the profile forbids is refused with its line; `--profile` chooses the profile QIR is written under. A rotation at any angle is read at its exact double, and a note says the bit-exact method refuses it. Every other verb reads QIR and jeff by the file's extension, or by `--from qir` or `--from jeff`, the reader's notes on standard error. A construct a reader cannot hold exactly is refused with a code from `Q700` and its place; `wai-quantum diagnostics` lists them, and [the formats page](../wai-quantum/docs/formats.md) says what each reader takes. + ## Your own hardware Bring your own quantum: your device, your account with a provider, or a simulator you trust, listed in your registry with its credential in the operating system's keychain ([the guide](../wai-quantum-qpu/bring-your-own-quantum.md), [the protocol](../extensions/quantum-providers.md)). @@ -348,12 +360,13 @@ wai-quantum verify sweep.json # the set's receipt, each item's, the r `wai-quantum mcp` is a Model Context Protocol server on standard input and output. Its tools are: -- `run_quantum_circuit` (with `intent` and `repair`; its verdict in every answer) +- `run_quantum_circuit` (with `intent` and `repair`; its verdict in every answer; its program as `qasm`, or in another format as `source`, as the job service takes it) - `plan_quantum_circuit` - `lint_qasm` - `repair_qasm` (verdicts and repairs, above) - `critique_qasm` - `check_equivalent` +- `convert_program` - `improve_qasm` - `classical_shortcuts` - `compile_qasm` (compiling, above) diff --git a/wai/wai-quantum-cli/README.zh-CN.md b/wai/wai-quantum-cli/README.zh-CN.md index 4c89b3d9b..ec39a8c06 100644 --- a/wai/wai-quantum-cli/README.zh-CN.md +++ b/wai/wai-quantum-cli/README.zh-CN.md @@ -220,6 +220,18 @@ wai-quantum shortcuts prog.qasm # 模拟它的每一条精确经典 存在一条便宜的途径,就证明该设计在经典上是容易的。没有一条途径便宜,并不能证明任何关于其困难性的结论:其他方法,尤其是近似方法,代价可能低得多。 +## 其他格式 + +```sh runnable +wai-quantum convert prog.qasm --to qir > prog.ll # QIR 2.0,基础配置文件(分支的程序则用自适应配置文件) +wai-quantum convert prog.ll --to jeff -o prog.jeff # jeff;所读格式由文件判断 +wai-quantum convert prog.jeff --to qasm # 转回 OpenQASM 3 +wai-quantum run prog.ll --shots 100 # 每个命令都能读取 QIR(.ll、.qir)和 jeff(.jeff) +wai-quantum equivalent prog.jeff prog.qasm # 退出状态 0:同一条线路 +``` + +`convert` 在 OpenQASM 3、QIR(LLVM 文本)和 jeff 之间转换程序,并检查每一次转换:写出的结果会被读回,并由等价检查与原程序比对,标准错误上的 `check:` 说明是如何判定的(`--no-check` 跳过检查;`--json` 以数据形式给出全部内容,jeff 以 base64 给出)。QIR 按其入口点所声明的配置文件(base 或 adaptive)读取,配置文件禁止的内容按行拒绝;`--profile` 选择写出 QIR 时所用的配置文件。任意角度的旋转按其精确的双精度值读取,并附注说明逐位精确的方法不接受它。其他每个命令都按文件扩展名读取 QIR 和 jeff,或按 `--from qir`、`--from jeff` 读取,读取器的说明输出到标准错误。读取器无法精确表示的构造会以 `Q700` 起的代码及其位置拒绝;`wai-quantum diagnostics` 列出这些代码,[格式说明页](../wai-quantum/docs/formats.md)说明每个读取器接受什么。 + ## 你自己的硬件 自带你的量子资源:你的设备、你在某个提供方处的账户,或你信任的模拟器,登记在你的注册表中,其凭据存放在操作系统的钥匙串里([指南](../wai-quantum-qpu/bring-your-own-quantum.md)、[协议](../extensions/quantum-providers.md))。 @@ -346,12 +358,13 @@ wai-quantum verify sweep.json # 程序集的回执、每一项的回 `wai-quantum mcp` 是一个基于标准输入输出的 Model Context Protocol(MCP)服务器。它的工具有: -- `run_quantum_circuit`(带 `intent` 和 `repair`;每个回答中都有其判定) +- `run_quantum_circuit`(带 `intent` 和 `repair`;每个回答中都有其判定;程序以 `qasm` 给出,或以其他格式作为 `source` 给出,与作业服务的接受方式相同) - `plan_quantum_circuit` - `lint_qasm` - `repair_qasm`(判定与修复,见上文) - `critique_qasm` - `check_equivalent` +- `convert_program` - `improve_qasm` - `classical_shortcuts` - `compile_qasm`(编译,见上文) diff --git a/wai/wai-quantum-cli/docs/llms-full.txt b/wai/wai-quantum-cli/docs/llms-full.txt index 739c20fc9..92845372d 100644 --- a/wai/wai-quantum-cli/docs/llms-full.txt +++ b/wai/wai-quantum-cli/docs/llms-full.txt @@ -46,7 +46,8 @@ Runs on this machine, signed with the user's key; nothing leaves the machine. Run an OpenQASM program on this machine, planned as the quantum job service plans it: measurement counts, Pauli expectation values and/or amplitudes, the method chosen and why, its accuracy class, and a receipt signed with this user's key. Give `intent` (an exact distribution, or a reference program it must equal or whose state it must prepare) and the engine checks it with the run. Every answer carries `verdict`: the level the engine checked to (proof, evidence, executed, parsed, refused), whether the intent holds and a plain statement, sealed in the receipt. Quote the verdict and the numbers; do not estimate them or claim more. Parameters: -- `qasm` (string, required): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. +- `qasm` (string): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. - `shots` (integer): Shots to sample; 1024 when nothing else is asked. - `seed` (integer): The sampling seed (default 1). @@ -67,7 +68,8 @@ Parameters: The method a program would run on and why, the memory and work it would take, without running it. Parameters: -- `qasm` (string, required): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. +- `qasm` (string): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. - `shots` (integer): Shots to sample; 1024 when nothing else is asked. - `seed` (integer): The sampling seed (default 1). @@ -122,6 +124,18 @@ Parameters: - `b` (string, required): The second program. - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. +### `convert_program`: Convert a program between formats + +Carry a program between OpenQASM 3, QIR (LLVM text, read under its base and adaptive profiles, written as QIR 2.0) and jeff (its Cap'n Proto message, as base64). The answer is the program written, the program as OpenQASM 3 (what every other tool takes), each note with its code (a rotation at an arbitrary angle, a custom gate read by its name, what a format leaves out), what was read and written, and the check: the output read back and held to the program by the equivalence ladder. A construct a reader cannot hold exactly is refused with its code (Q700 to Q721) and its place: quote the code, and never guess around it. + +Parameters: +- `program` (string): The program's text: OpenQASM or QIR. +- `program_base64` (string): The program's bytes as base64: jeff. +- `from` (string, one of qasm, qir, jeff): Its format; found from its bytes when not given. +- `to` (string, required, one of qasm, qir, jeff): The format to write. +- `profile` (string, one of base, adaptive): QIR's profile to write: by default base when the program's measurements all come at the end, else adaptive. +- `check` (boolean): false: write it without the check. + ### `improve_qasm`: Improve an OpenQASM program The program with its smells fixed in its own text (all of them, and each alone), each with its measures and the equivalence check's verdict against the original. A candidate the check finds different is never returned. @@ -396,7 +410,8 @@ Parameters: Run an OpenQASM program here as it would run on a device target: compiled exactly to the target's native gates, placed on the qubits and routes most likely to succeed (the expected success given beside the fewest-swaps layout's), checked against the circuit, and simulated under the noise its calibration gives (each gate's error, T1, T2 and durations, idling, and each qubit's readout), on the density matrix or by trajectories. Counts and expectation values, their distance from the ideal distribution, the noise derived, and a receipt signed with this user's key that binds the target's hash. A model of the device, not a run on it: quote it as such. Every angle must be a dyadic multiple of 2π. Parameters: -- `qasm` (string, required): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. +- `qasm` (string): The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in "inputs"). while, inv and pow are refused with their line. - `inputs` (object): Values for a generator's integer inputs, as {"n": 5}: the program is read as its family's circuit at those values. - `shots` (integer): Shots to sample; 1024 when nothing else is asked. - `seed` (integer): The sampling seed (default 1). @@ -508,7 +523,7 @@ Streamable HTTP, no account and no key. Run an OpenQASM program and return measurement counts, Pauli expectation values and/or amplitudes. The service chooses the method (bit-exact or double-precision state vector up to 32 qubits, single precision on a GPU to 33, stabilizer tableau for Clifford circuits up to 32768 qubits, matrix product states up to 4096, sparse Pauli dynamics for expectation values up to 1024 with an error bound, an exact density matrix or trajectories for noise), reports why and how accurate, and signs the result. Give `intent` (an exact distribution, or a reference program it must equal or whose state it must prepare) and the engine checks it with the run. Every answer carries `verdict`: the level the engine checked to (proof, evidence, executed, parsed, refused), whether the intent holds and a plain statement, sealed in the receipt. Waits up to 50 s; a longer job answers with status running and an id for get_quantum_job. Parameters: -- `qasm` (string, required): The program, OpenQASM 3 (or 2), read as the whole language: registers, aliases and slices; the standard gates, `U` and `gphase`; user `gate` definitions and `def` subroutines, inlined; every modifier (`ctrl @`, `negctrl @`, `inv @`, `pow(k) @`) on any gate, lowered exactly; classical types (int, uint, float, angle, complex, bool, bit, arrays), their arithmetic and built-in functions, computed on reading; `for`, `while`, `switch`, `break`, `continue`; measurement and `reset` anywhere; classical control on measured bits (`if (c[0]) x q[1];`, `if (c >= 2) { … } else { … }`, `&&`, `||`); a `while` on measured bits takes `@wai.max_iterations N` before it. `output` declarations come back in the result; `delay`, `box` and `stretch` are carried and labelled as not applied; `defcal` and `cal` are kept, not run. A program that measures in the middle or acts on what it measured runs on statevector or stabilizer: counts split their shots at each measurement; expectation values are exact, of the state before the final measurements. What the reader refuses comes back with a code, as `[Q103]`; qasm_diagnostics says what each code means. +- `qasm` (string): The program, OpenQASM 3 (or 2), read as the whole language: registers, aliases and slices; the standard gates, `U` and `gphase`; user `gate` definitions and `def` subroutines, inlined; every modifier (`ctrl @`, `negctrl @`, `inv @`, `pow(k) @`) on any gate, lowered exactly; classical types (int, uint, float, angle, complex, bool, bit, arrays), their arithmetic and built-in functions, computed on reading; `for`, `while`, `switch`, `break`, `continue`; measurement and `reset` anywhere; classical control on measured bits (`if (c[0]) x q[1];`, `if (c >= 2) { … } else { … }`, `&&`, `||`); a `while` on measured bits takes `@wai.max_iterations N` before it. `output` declarations come back in the result; `delay`, `box` and `stretch` are carried and labelled as not applied; `defcal` and `cal` are kept, not run. A program that measures in the middle or acts on what it measured runs on statevector or stabilizer: counts split their shots at each measurement; expectation values are exact, of the state before the final measurements. What the reader refuses comes back with a code, as `[Q103]`; qasm_diagnostics says what each code means. - `shots` (integer): Measurement shots. Default 1024 when no observables or amplitudes are asked for. - `observables` (array): Pauli strings to take expectation values of, e.g. ["Z0", "Z0 Z1", "X2 Y3"]. Qubits count from 0. - `amplitudes` (array): Basis states to give amplitudes of: one character per qubit, qubit n-1 first, e.g. "0011". @@ -522,6 +537,7 @@ Parameters: - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's `repaired` holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. - `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. ### `run_quantum_set`: Run a set of quantum programs, or one over a grid @@ -566,6 +582,7 @@ Parameters: - `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. - `programs` (array): The set's programs: each a program's text, or a job ({"qasm", …}) whose own fields win over the set's. - `sweep` (object): Each input of qasm and its values: every combination runs, the names in sorted order, the first varying slowest; an integer input's values are whole numbers. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. ### `get_quantum_job`: Read a quantum job @@ -597,6 +614,18 @@ Parameters: - `a` (string, required) - `b` (string, required) +### `convert_program`: Convert a program between formats + +Carry a program between OpenQASM 3, QIR (LLVM text, read under its base and adaptive profiles, written as QIR 2.0) and jeff (its Cap'n Proto message, as base64). The answer is the program written, the program as OpenQASM 3 (what every other tool takes), each note with its code (a rotation at an arbitrary angle, a custom gate read by its name, what a format leaves out), what was read and written, and the check: the output read back and held to the program by the equivalence ladder. A construct a reader cannot hold exactly is refused with its code (Q700 to Q721) and its place: quote the code, and never guess around it. A conversion larger than the service takes is refused with the command that runs it locally. + +Parameters: +- `program` (string): The program's text: OpenQASM or QIR. +- `program_base64` (string): The program's bytes as base64: jeff. +- `from` (string, one of qasm, qir, jeff): Its format; found from its bytes when not given. +- `to` (string, required, one of qasm, qir, jeff): The format to write. +- `profile` (string, one of base, adaptive): QIR's profile to write: by default base when the program's measurements all come at the end, else adaptive. +- `check` (boolean): false: write it without the check. + ### `improve_qasm`: Improve a quantum program The program with its smells fixed in its own text (all of them, and each alone), each candidate with its measures and the equivalence check's verdict against the program. A candidate the check finds different is never returned. @@ -677,12 +706,12 @@ Parameters: Run an OpenQASM program as it would run on a device target: compiled exactly to its gates, placed on its most reliable qubits and routes (the expected success given), checked against the circuit, and simulated under the noise its calibration gives, on the density matrix or by trajectories. Counts and expectation values with their distance from the ideal distribution, the noise derived, and a receipt that binds the target's hash. A model of the device, not a run on it. Every angle must be a dyadic multiple of 2π. Waits up to 50 s, as run_quantum_circuit does. Parameters: -- `qasm` (string, required): The program, OpenQASM 3 (or 2), read as the whole language: registers, aliases and slices; the standard gates, `U` and `gphase`; user `gate` definitions and `def` subroutines, inlined; every modifier (`ctrl @`, `negctrl @`, `inv @`, `pow(k) @`) on any gate, lowered exactly; classical types (int, uint, float, angle, complex, bool, bit, arrays), their arithmetic and built-in functions, computed on reading; `for`, `while`, `switch`, `break`, `continue`; measurement and `reset` anywhere; classical control on measured bits (`if (c[0]) x q[1];`, `if (c >= 2) { … } else { … }`, `&&`, `||`); a `while` on measured bits takes `@wai.max_iterations N` before it. `output` declarations come back in the result; `delay`, `box` and `stretch` are carried and labelled as not applied; `defcal` and `cal` are kept, not run. A program that measures in the middle or acts on what it measured runs on statevector or stabilizer: counts split their shots at each measurement; expectation values are exact, of the state before the final measurements. What the reader refuses comes back with a code, as `[Q103]`; qasm_diagnostics says what each code means. +- `qasm` (string): The program, OpenQASM 3 (or 2), read as the whole language: registers, aliases and slices; the standard gates, `U` and `gphase`; user `gate` definitions and `def` subroutines, inlined; every modifier (`ctrl @`, `negctrl @`, `inv @`, `pow(k) @`) on any gate, lowered exactly; classical types (int, uint, float, angle, complex, bool, bit, arrays), their arithmetic and built-in functions, computed on reading; `for`, `while`, `switch`, `break`, `continue`; measurement and `reset` anywhere; classical control on measured bits (`if (c[0]) x q[1];`, `if (c >= 2) { … } else { … }`, `&&`, `||`); a `while` on measured bits takes `@wai.max_iterations N` before it. `output` declarations come back in the result; `delay`, `box` and `stretch` are carried and labelled as not applied; `defcal` and `cal` are kept, not run. A program that measures in the middle or acts on what it measured runs on statevector or stabilizer: counts split their shots at each measurement; expectation values are exact, of the state before the final measurements. What the reader refuses comes back with a code, as `[Q103]`; qasm_diagnostics says what each code means. - `shots` (integer): Measurement shots. Default 1024 when no observables or amplitudes are asked for. - `observables` (array): Pauli strings to take expectation values of, e.g. ["Z0", "Z0 Z1", "X2 Y3"]. Qubits count from 0. - `amplitudes` (array): Basis states to give amplitudes of: one character per qubit, qubit n-1 first, e.g. "0011". - `noise` (object): Optional noise, each a probability: depolarizing_1q, depolarizing_2q, amplitude_damping, dephasing (after each gate on its qubits), readout (each measured bit). -- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. +- `source` (object): The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source. - `seed` (integer): Sampling seed (default 1); the same job and seed give the same counts. - `options` (object): Advanced, per method: max_bond (mps), threshold and max_terms (pauli), fusion (statevector), trajectories. - `compile` (object): Compile the program first (as compile_qasm does) and run the compiled circuit: with basis clifford_t it runs bit-exact, and the result's `compiled` states its distance from the program. @@ -690,6 +719,7 @@ Parameters: - `intent` (any): What the program must do, checked by the engine with the run: the result's `verdict` states how far it checked (proof, evidence, executed, parsed, refused) and whether the intent holds, sealed by the receipt. - `repair` (any): Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {"max_iterations": N, "dyadic_bits": K, "sizes": {"q": 3}}. The result's `repaired` holds the program that ran and each repair. - `mitigate` (any): Mitigate the job's expectation values (or, for readout, its counts) under its own noise: zne (the whole program folded U(U†U)^k and extrapolated), pea (the noise learned as a sparse Pauli–Lindblad model and amplified), pec (the learned channels inverted and sampled), cdr (a line fitted over near-Clifford training circuits), vd (two copies), readout (the counts' flips inverted). Needs noise and observables (shots for readout), at most 8 qubits. The result's `mitigation` gives raw and mitigated values with standard errors, the ideal value (known because this is a simulation), the method's bias and variance assumptions and its cost. A method's name, or an object. +- `pulse` (object): A pulse run's options (method "pulse"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0. ### `quantum_circuit_examples`: Ready-to-run quantum examples @@ -1068,6 +1098,25 @@ Every code: | `Q502` | refused | A program that measures a qubit and then acts on it, resets one after its first gate, or acts on measured bits: the bit-exact circuit takes programs whose measurements all come at the end. | | `Q600` | carried | An `output` declaration, carried into the result: its bits (their places in each outcome) or its value. | | `Q601` | refused | An `output` that is not a bit and whose value depends on a measurement at the end. | +| `Q700` | refused | A file that does not read in the format named: QIR that is not LLVM text with one entry point, or jeff that is not a Cap'n Proto message of the jeff schema (a pointer outside the message, a struct or list of the wrong shape, more than the reader takes); the message says what and where. | +| `Q701` | refused | A format version the reader does not take: QIR whose `qir_major_version` is not 1 or 2, or jeff other than 0.2 and 0.3. | +| `Q702` | warned | A QIR module that leaves out what its profile requires (the `qir_profiles` attribute, the version flags, `required_num_qubits`, `required_num_results`, the `entry_point` attribute of its only function): read from what it holds, and said so. | +| `Q703` | refused | A QIR profile the reader does not take: `qir_profiles` names neither `base_profile` nor `adaptive_profile`. | +| `Q704` | refused | What the module's own QIR profile forbids: under the base profile a branch, a classical instruction, reading a result, or a gate or reset on a qubit after its measurement; under the adaptive profile an optional capability its module flags do not declare (computation on integers or floats of a type, IR-defined functions, loops, `switch`, several returns, dynamic qubits or results, arrays). | +| `Q705` | refused | A QIR capability the module may use that this reader does not take: loops, `switch`, `phi`, IR-defined functions, dynamic allocation of qubits or results, arrays, controlled forms that take their controls in an array, or a classical value computed from a measurement anywhere but a branch's condition. | +| `Q706` | refused | An instruction or function the reader does not know: a quantum intrinsic outside the gates it reads (`docs/formats.md` lists them), a runtime function it does not take, an LLVM instruction no profile lists, or a function a module declares and does not define. | +| `Q707` | refused | A QIR operand that does not read: a qubit or result that is not a constant index, an index past `required_num_qubits` or `required_num_results`, or an angle that is not a finite number known on reading. | +| `Q708` | refused | A value whose meaning the program's text cannot keep: in QIR a result read, measured again and only then branched on; in jeff a qubit the two sides of a branch on a measurement leave in different places. | +| `Q709` | warned | Rotations at angles that are not dyadic multiples of 2π, read at their exact doubles: the double-precision methods take them; the bit-exact method refuses them (Q500). | +| `Q710` | refused | A jeff custom gate the reader does not know: its name is not a gate of the standard library (in any case), or its qubits or parameters are not that gate's. | +| `Q711` | warned | A jeff custom gate named as a gate of the standard library (`H`, `CX`, `Rx`, in any case) with that gate's qubits and parameters: read as that gate, by its name alone. | +| `Q712` | lowered | A jeff Pauli-product rotation, `exp(iθP)` as its schema defines it, lowered exactly to basis changes, a parity ladder and one `rz`. | +| `Q713` | refused | A jeff value used where it cannot be: a qubit or register used twice, a value used before it is defined or never defined, a cycle, or a value of the wrong type or count for its operation. | +| `Q714` | refused | A jeff value the reader needs on reading and cannot know: a gate angle, a loop's bounds or a `while` condition from a measurement, a register's size or index, a `switch` on a value that is neither known nor a measured bit, or integer arithmetic on a measured bit beyond not, and, or, xor, eq and select. | +| `Q715` | lowered | jeff's structured control flow: a `for` or `while` decided on reading unrolled, a `switch` on a known value decided, a `switch` on a measured bit written as `if … else`, and a call of a function the module defines inlined. | +| `Q716` | warned | A jeff entry point that takes qubits: they are the program's first qubits, starting in \|0⟩, and the qubits it returns are not measured. | +| `Q720` | refused | A program the format written cannot hold as it is: under QIR's base profile a measurement in the middle, a reset after a gate or a condition (write the adaptive profile), a loop in QIR, or in jeff a branch on a measured bit. | +| `Q721` | warned | What the format read or written has no place for, left out and said so: delays, boxes, barriers and gate durations, calibrations, `output` declarations, and classical values recorded as output. | ### `wai-quantum://docs/methods`: the methods and their accuracy classes @@ -1335,6 +1384,10 @@ Commands: run Run a program here, planned as the job service plans it, and sign the result plan Show the method a program would run on and why, without running it instance Write a generator out at its `--input` values, as plain OpenQASM + convert Carry a program between formats (OpenQASM 3, QIR as LLVM text, jeff), every + conversion checked: what is written is read back and held to the program by the + equivalence ladder; notes and the check go to standard error (exit 1 refused, 2 the + check found it different) lint Check a program and print each problem with its line draw Draw a program's circuit: as text, or as an SVG image, LaTeX (plain TikZ) or words debug Step through a program on the engine's exact methods and look into the state at a @@ -1444,6 +1497,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1477,6 +1534,10 @@ Options: --seed The seed shots are sampled with (1 if not given) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --method A method by name, or auto (the default) for the strongest the program admits; `wai-quantum methods` lists them @@ -1607,6 +1668,10 @@ Options: --seed The seed shots are sampled with (1 if not given) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --method A method by name, or auto (the default) for the strongest the program admits; `wai-quantum methods` lists them @@ -1707,6 +1772,61 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + + -h, --help + Print help + + -V, --version + Print version +``` + +### `wai-quantum convert` + +```text +Carry a program between formats (OpenQASM 3, QIR as LLVM text, jeff), every conversion checked: what +is written is read back and held to the program by the equivalence ladder; notes and the check go to +standard error (exit 1 refused, 2 the check found it different) + +Usage: wai-quantum convert [OPTIONS] --to [FILE] + +Arguments: + [FILE] + The program; `-` or nothing reads standard input; its format is --from, else its + extension, else what its bytes look like + +Options: + --input + A value for a generator's integer `input`, as n=5 (repeat for each input): the program is + read as its family's circuit at those values + + --to + The format to write: qasm, qir or jeff + + --lang + The language of the words the command line writes itself, en or zh-CN (by default the one + LANG names); results, codes and the engine's own messages stay in English + + --profile + QIR's profile, base or adaptive: by default the base profile when the program's + measurements all come at the end, else the adaptive one + + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + + -o, --output + Write the program to this file (jeff is binary: give a file, or --json for its base64) + + --json + Print the conversion as JSON: the program (base64 for jeff), the program as OpenQASM, the + notes, what was read and written, and the check + + --no-check + Write it without the check + -h, --help Print help @@ -1737,6 +1857,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1772,6 +1896,10 @@ Options: --svg The drawing as a standalone SVG image, the Studio's picture + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --latex The drawing as LaTeX: one picture in plain TikZ, which needs only \usepackage{tikz} @@ -1813,6 +1941,10 @@ Options: --line Stop before the first step on this line + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --outcomes The outcome of each measurement (and reset) the path meets, in order, as 0,1,1; the likelier when not given @@ -1867,6 +1999,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1898,6 +2034,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1932,6 +2072,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1964,6 +2108,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -1999,6 +2147,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2046,6 +2198,10 @@ Options: [default: clifford_t] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --twirl Twirl each two-qubit Clifford gate with Paulis drawn from this seed (one instance; checked to be the same gate) @@ -2113,6 +2269,10 @@ Options: [default: 0.001] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --qubit A reference qubit (us-e3, us-e4, ns-e3, ns-e4, meas-e4, meas-e6), `all` of them, or a JSON file describing one @@ -2189,6 +2349,10 @@ Options: Round each angle the bit-exact method refuses (Q500) to the nearest multiple of 2π/2^K, its rounding stated on its line + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --size A register's size, for one used before it is declared (Q102), as q=3 (repeatable) @@ -2236,6 +2400,10 @@ Options: --seed The seed shots are sampled with (1 if not given) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --method A method by name, or auto (the default) for the strongest the program admits; `wai-quantum methods` lists them @@ -2373,6 +2541,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2413,6 +2585,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2453,6 +2629,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2497,6 +2677,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2542,6 +2726,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2586,6 +2774,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2631,6 +2823,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2675,6 +2871,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2715,6 +2915,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --service The service's address @@ -2762,6 +2966,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2792,6 +3000,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2819,6 +3031,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2850,6 +3066,10 @@ Options: [default: 16] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2877,6 +3097,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -2909,6 +3133,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --memory-gb The memory a job may take, GiB @@ -2956,6 +3184,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --memory-gb The memory a job may take, GiB, when not the server's default @@ -2991,6 +3223,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --name The name the entry goes under @@ -3043,6 +3279,10 @@ Options: --write Write each file back formatted + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3080,6 +3320,10 @@ Options: Sign with the published demo key: the receipt then shows a result is unaltered, not who signed it + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --stdio Accepted for the editors that pass it: standard input and output is the server's only transport @@ -3119,6 +3363,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3156,6 +3404,10 @@ Options: Sign with the published demo key: the receipt then shows a result is unaltered, not who signed it + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3191,6 +3443,10 @@ Options: [default: 1] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --max-edits Edits made to a parent before it is evaluated, at most @@ -3249,6 +3505,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3285,6 +3545,10 @@ Options: --parent The id of the archive entry it came from + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --proposer Who wrote it @@ -3336,6 +3600,10 @@ Options: --source Print one entry's generator, by id + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json Print the summary as JSON @@ -3369,6 +3637,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3406,6 +3678,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3444,6 +3720,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3482,6 +3762,10 @@ Options: [default: person] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --name Which one: a model's or an agent's name, the rewrite, where the program came from @@ -3528,6 +3812,10 @@ Options: --locks The rules a proposal must keep (default: beside the program, as NAME.locks.json, if there) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --parent The version it was changed from (default: the head) @@ -3585,6 +3873,10 @@ Options: --locks The rules a proposal must keep (default: beside the program, as NAME.locks.json, if there) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --parent @@ -3630,6 +3922,10 @@ Options: --accept The changes accepted: their numbers from `changes` (as 0,2), all, or none + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --parent @@ -3695,6 +3991,10 @@ Options: [default: person] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --name Which one: a model's or an agent's name, the rewrite, where the program came from @@ -3741,6 +4041,10 @@ Options: --locks The rules a proposal must keep (default: beside the program, as NAME.locks.json, if there) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json @@ -3780,6 +4084,10 @@ Options: --locks The rules a proposal must keep (default: beside the program, as NAME.locks.json, if there) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json @@ -3817,6 +4125,10 @@ Options: --locks The rules a proposal must keep (default: beside the program, as NAME.locks.json, if there) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --job A job from `run --json --history` or the service @@ -3855,6 +4167,10 @@ Options: The program it is meant to improve on: the measures it beats it on that --weigh does not name + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --weigh The measures the cost weighs, comma-separated (as depth,two_qubit) @@ -3890,6 +4206,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3926,6 +4246,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3956,6 +4280,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -3989,6 +4317,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4023,6 +4355,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4060,6 +4396,10 @@ Options: Sign with the published demo key: the receipt then shows a result is unaltered, not who signed it + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4083,6 +4423,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4113,6 +4457,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4154,6 +4502,10 @@ Options: Sign with the published demo key: the receipt then shows a result is unaltered, not who signed it + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json @@ -4188,6 +4540,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4222,6 +4578,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4258,6 +4618,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4290,6 +4654,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4320,6 +4688,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4353,6 +4725,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4383,6 +4759,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4416,6 +4796,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4449,6 +4833,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4487,6 +4875,10 @@ Options: [default: equivalent] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --profile The profile's name: one person can keep several (for teaching, for one device) @@ -4526,6 +4918,10 @@ Options: --taste The file the profiles are kept in + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json @@ -4562,6 +4958,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --novelty The novelty dial: −1 toward the typical, 1 toward the novel @@ -4612,6 +5012,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --task `prepares` or `equivalent` (a specification's own task by default) @@ -4657,6 +5061,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4693,6 +5101,10 @@ Options: --check Check this certificate (a sealed answer or the certificate alone) against the code + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json Print the answer sealed with this user's key, as JSON (`wai-quantum verify` checks it) @@ -4735,6 +5147,10 @@ Options: --error The flipped qubits, as 3,12 (its syndrome is decoded and its residual judged) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --memory The rotated surface-code memory experiment, as DISTANCE,ROUNDS,P @@ -4801,6 +5217,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --p Physical rates, as 0.002,0.004 @@ -4857,6 +5277,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4893,6 +5317,10 @@ Options: [default: 60] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json Print the answer sealed with this user's key, as JSON (`wai-quantum verify` checks it) @@ -4941,6 +5369,10 @@ Options: Sign with the published demo key: the receipt then shows a result is unaltered, not who signed it + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -4974,6 +5406,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5006,6 +5442,10 @@ Options: The model server: a chat-completions endpoint on this machine or a private network, as http://127.0.0.1:8080/v1 + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --name The model's name as its server knows it (default: the server's own) @@ -5077,6 +5517,10 @@ Options: -o, --out Write the corpus here (default: standard output) + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5105,6 +5549,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5141,6 +5589,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5177,6 +5629,10 @@ Options: [default: 16] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --junit Also write the verdict as a JUnit report to this file, one test case for each intent @@ -5221,6 +5677,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5267,6 +5727,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help (see a summary with '-h') @@ -5304,6 +5768,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --json Print the course and your progress as JSON @@ -5337,6 +5805,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5370,6 +5842,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5404,6 +5880,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5440,6 +5920,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5474,6 +5958,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5503,6 +5991,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5537,6 +6029,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5574,6 +6070,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --provider Read one of your providers' describe answers instead of a file (`wai-quantum providers list`) @@ -5610,6 +6110,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5643,6 +6147,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --view Print what the Studio draws: each qubit's place and errors, each pair's error @@ -5673,6 +6181,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5696,6 +6208,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5735,6 +6251,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5773,6 +6293,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + --set A setting that is not a secret, as backend=NAME (repeatable) @@ -5813,6 +6337,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5840,6 +6368,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5871,6 +6403,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5905,6 +6441,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5937,6 +6477,10 @@ Options: [default: 16] + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5965,6 +6509,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help @@ -5992,6 +6540,10 @@ Options: The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English + --from + The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default + the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + -h, --help Print help diff --git a/wai/wai-quantum-cli/docs/llms.txt b/wai/wai-quantum-cli/docs/llms.txt index 996556f9f..b1f634681 100644 --- a/wai/wai-quantum-cli/docs/llms.txt +++ b/wai/wai-quantum-cli/docs/llms.txt @@ -47,6 +47,7 @@ Runs on this machine, signed with the user's key; nothing leaves the machine. - `repair_qasm`: Repair a program on its flagged lines - `critique_qasm`: Critique an OpenQASM program - `check_equivalent`: Check two programs are the same circuit +- `convert_program`: Convert a program between formats - `improve_qasm`: Improve an OpenQASM program - `classical_shortcuts`: Find a program's classical shortcuts - `compile_qasm`: Compile a program, every pass checked @@ -95,6 +96,7 @@ Runs on this machine, signed with the user's key; nothing leaves the machine. - `verify_quantum_receipt`: Verify a result's receipt - `critique_qasm`: Critique a quantum program - `check_equivalent`: Check two quantum programs are the same circuit + - `convert_program`: Convert a program between formats - `improve_qasm`: Improve a quantum program - `classical_shortcuts`: Find a quantum program's classical shortcuts - `compile_qasm`: Compile a quantum program, every pass checked diff --git a/wai/wai-quantum-cli/docs/local-mcp.json b/wai/wai-quantum-cli/docs/local-mcp.json index 8416d4036..813589482 100644 --- a/wai/wai-quantum-cli/docs/local-mcp.json +++ b/wai/wai-quantum-cli/docs/local-mcp.json @@ -7,10 +7,45 @@ "description": "Run an OpenQASM program on this machine, planned as the quantum job service plans it: measurement counts, Pauli expectation values and/or amplitudes, the method chosen and why, its accuracy class, and a receipt signed with this user's key. Give `intent` (an exact distribution, or a reference program it must equal or whose state it must prepare) and the engine checks it with the run. Every answer carries `verdict`: the level the engine checked to (proof, evidence, executed, parsed, refused), whether the intent holds and a plain statement, sealed in the receipt. Quote the verdict and the numbers; do not estimate them or claim more.", "inputSchema": { "type": "object", - "required": [ - "qasm" + "anyOf": [ + { + "required": [ + "qasm" + ] + }, + { + "required": [ + "source" + ] + } ], "properties": { + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format." + }, + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + } + } + }, "qasm": { "type": "string", "description": "The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in \"inputs\"). while, inv and pow are refused with their line." @@ -542,6 +577,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -549,6 +589,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -3368,6 +3432,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -3375,6 +3444,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -6795,10 +6888,45 @@ "description": "The method a program would run on and why, the memory and work it would take, without running it.", "inputSchema": { "type": "object", - "required": [ - "qasm" + "anyOf": [ + { + "required": [ + "qasm" + ] + }, + { + "required": [ + "source" + ] + } ], "properties": { + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format." + }, + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + } + } + }, "qasm": { "type": "string", "description": "The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in \"inputs\"). while, inv and pow are refused with their line." @@ -12570,6 +12698,274 @@ ] } }, + { + "name": "convert_program", + "title": "Convert a program between formats", + "description": "Carry a program between OpenQASM 3, QIR (LLVM text, read under its base and adaptive profiles, written as QIR 2.0) and jeff (its Cap'n Proto message, as base64). The answer is the program written, the program as OpenQASM 3 (what every other tool takes), each note with its code (a rotation at an arbitrary angle, a custom gate read by its name, what a format leaves out), what was read and written, and the check: the output read back and held to the program by the equivalence ladder. A construct a reader cannot hold exactly is refused with its code (Q700 to Q721) and its place: quote the code, and never guess around it.", + "inputSchema": { + "type": "object", + "required": [ + "to" + ], + "properties": { + "program": { + "type": "string", + "description": "The program's text: OpenQASM or QIR." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + }, + "from": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format; found from its bytes when not given." + }, + "to": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "The format to write." + }, + "profile": { + "type": "string", + "enum": [ + "base", + "adaptive" + ], + "description": "QIR's profile to write: by default base when the program's measurements all come at the end, else adaptive." + }, + "check": { + "type": "boolean", + "description": "false: write it without the check." + } + } + }, + "outputSchema": { + "$id": "wai-quantum://schemas/convert_program/1", + "type": "object", + "description": "A program carried from one format to another (OpenQASM 3, QIR as LLVM text, jeff as its Cap'n Proto message) and checked (engine::formats::convert_json): the program written, the program as read in OpenQASM 3, the readers' and writer's notes with their codes, what was read and written, and the check that holds the output to the input.", + "anyOf": [ + { + "type": "object", + "description": "A program carried from one format to another (OpenQASM 3, QIR as LLVM text, jeff as its Cap'n Proto message) and checked (engine::formats::convert_json): the program written, the program as read in OpenQASM 3, the readers' and writer's notes with their codes, what was read and written, and the check that holds the output to the input.", + "required": [ + "ok", + "format", + "from", + "to", + "qasm", + "notes", + "read", + "written", + "check" + ], + "properties": { + "ok": { + "const": true + }, + "format": { + "const": "wai.quantum.convert/1" + }, + "from": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "to": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program written, for a text format (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program written, for a binary format (jeff), as standard base64." + }, + "qasm": { + "type": "string", + "description": "The program as read, as OpenQASM 3: what every other tool takes." + }, + "notes": { + "type": "array", + "items": { + "type": "object", + "description": "One of the reader's diagnostics: its code, what happens to the construct it is about, its message (without the line or the code), and where it stands.", + "required": [ + "code", + "outcome", + "message" + ], + "properties": { + "code": { + "type": "string" + }, + "outcome": { + "enum": [ + "refused", + "lowered", + "carried", + "preserved", + "warned" + ] + }, + "message": { + "type": "string" + }, + "line": { + "type": "integer", + "minimum": 1 + }, + "column": { + "type": "integer", + "minimum": 1 + }, + "length": { + "type": "integer", + "minimum": 0 + } + } + }, + "description": "Each warning and note, with its code (Q7xx for the formats) and, for QIR, its line." + }, + "read": { + "type": "object", + "required": [ + "format" + ], + "description": "What was read: the format, its version, a QIR module's profile, entry point, qubits, results and declared capabilities, a jeff module's tool, entry point, qubits and bits." + }, + "written": { + "type": "object", + "required": [ + "format" + ], + "description": "What was written: the format, its version, and QIR's profile." + }, + "check": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "required": [ + "what", + "verdict" + ], + "properties": { + "what": { + "type": "string", + "description": "What was held to what." + }, + "verdict": { + "type": "object", + "required": [ + "verdict", + "how", + "why" + ], + "properties": { + "verdict": { + "enum": [ + "equivalent", + "different", + "unchecked" + ] + }, + "how": { + "enum": [ + "tableau", + "exact", + "rewrites", + "random", + "state", + "shape", + "steps", + "distribution", + "identity", + "none" + ], + "description": "The equivalence ladder's check (tableau, exact and rewrites are proofs; random is evidence); steps: the same steps one for one; distribution: every outcome's probability the same (evidence); identity: OpenQASM passed through; none: unchecked." + }, + "why": { + "type": "string" + }, + "global_phase": { + "type": [ + "number", + "null" + ] + } + } + } + } + } + ], + "description": "The output read back and held to the program read (or, for a program read into OpenQASM, written back to its format and read again), or null when no check was asked." + } + } + }, + { + "type": "object", + "description": "A refusal as data: the message, every code in it (the last is the code), its outcome and meaning as the code's table gives them. A host that refuses for its own reasons gives the error alone.", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "codes": { + "type": "array", + "items": { + "type": "string" + } + }, + "outcome": { + "enum": [ + "refused", + "retry", + "failed", + "lowered", + "carried", + "preserved", + "warned", + "dropped", + "defaulted" + ] + }, + "meaning": { + "type": "string" + } + } + } + ] + } + }, { "name": "improve_qasm", "title": "Improve an OpenQASM program", @@ -26758,11 +27154,45 @@ "description": "Run an OpenQASM program here as it would run on a device target: compiled exactly to the target's native gates, placed on the qubits and routes most likely to succeed (the expected success given beside the fewest-swaps layout's), checked against the circuit, and simulated under the noise its calibration gives (each gate's error, T1, T2 and durations, idling, and each qubit's readout), on the density matrix or by trajectories. Counts and expectation values, their distance from the ideal distribution, the noise derived, and a receipt signed with this user's key that binds the target's hash. A model of the device, not a run on it: quote it as such. Every angle must be a dyadic multiple of 2π.", "inputSchema": { "type": "object", - "required": [ - "qasm", - "target" + "anyOf": [ + { + "required": [ + "qasm" + ] + }, + { + "required": [ + "source" + ] + } ], "properties": { + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format." + }, + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + } + } + }, "qasm": { "type": "string", "description": "The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in \"inputs\"). while, inv and pow are refused with their line." @@ -27224,7 +27654,10 @@ } } } - } + }, + "required": [ + "target" + ] }, "outputSchema": { "$id": "wai-quantum://schemas/run_on_target/1", @@ -27294,6 +27727,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -27301,6 +27739,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -30120,6 +30582,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -30127,6 +30594,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -36482,6 +36973,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -36489,6 +36985,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -39308,6 +39828,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -39315,6 +39840,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, diff --git a/wai/wai-quantum-cli/docs/service-mcp.json b/wai/wai-quantum-cli/docs/service-mcp.json index 287f33231..b6629f0d7 100644 --- a/wai/wai-quantum-cli/docs/service-mcp.json +++ b/wai/wai-quantum-cli/docs/service-mcp.json @@ -7,8 +7,17 @@ "description": "Run an OpenQASM program and return measurement counts, Pauli expectation values and/or amplitudes. The service chooses the method (bit-exact or double-precision state vector up to 32 qubits, single precision on a GPU to 33, stabilizer tableau for Clifford circuits up to 32768 qubits, matrix product states up to 4096, sparse Pauli dynamics for expectation values up to 1024 with an error bound, an exact density matrix or trajectories for noise), reports why and how accurate, and signs the result. Give `intent` (an exact distribution, or a reference program it must equal or whose state it must prepare) and the engine checks it with the run. Every answer carries `verdict`: the level the engine checked to (proof, evidence, executed, parsed, refused), whether the intent holds and a plain statement, sealed in the receipt. Waits up to 50 s; a longer job answers with status running and an id for get_quantum_job.", "inputSchema": { "type": "object", - "required": [ - "qasm" + "anyOf": [ + { + "required": [ + "qasm" + ] + }, + { + "required": [ + "source" + ] + } ], "properties": { "qasm": { @@ -468,6 +477,32 @@ } } } + }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format." + }, + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + } + } } } }, @@ -547,6 +582,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -554,6 +594,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -3373,6 +3437,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -3380,6 +3449,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -8723,6 +8816,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -8730,6 +8828,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -11549,6 +11671,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -11556,6 +11683,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -16820,6 +16971,32 @@ } }, "description": "Each input of qasm and its values: every combination runs, the names in sorted order, the first varying slowest; an integer input's values are whole numbers." + }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format." + }, + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + } + } } } }, @@ -17903,6 +18080,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -17910,6 +18092,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -20729,6 +20935,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -20736,6 +20947,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -28702,6 +28937,324 @@ ] } }, + { + "name": "convert_program", + "title": "Convert a program between formats", + "description": "Carry a program between OpenQASM 3, QIR (LLVM text, read under its base and adaptive profiles, written as QIR 2.0) and jeff (its Cap'n Proto message, as base64). The answer is the program written, the program as OpenQASM 3 (what every other tool takes), each note with its code (a rotation at an arbitrary angle, a custom gate read by its name, what a format leaves out), what was read and written, and the check: the output read back and held to the program by the equivalence ladder. A construct a reader cannot hold exactly is refused with its code (Q700 to Q721) and its place: quote the code, and never guess around it. A conversion larger than the service takes is refused with the command that runs it locally.", + "inputSchema": { + "type": "object", + "required": [ + "to" + ], + "properties": { + "program": { + "type": "string", + "description": "The program's text: OpenQASM or QIR." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." + }, + "from": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "Its format; found from its bytes when not given." + }, + "to": { + "type": "string", + "enum": [ + "qasm", + "qir", + "jeff" + ], + "description": "The format to write." + }, + "profile": { + "type": "string", + "enum": [ + "base", + "adaptive" + ], + "description": "QIR's profile to write: by default base when the program's measurements all come at the end, else adaptive." + }, + "check": { + "type": "boolean", + "description": "false: write it without the check." + } + } + }, + "annotations": { + "readOnlyHint": true + }, + "outputSchema": { + "$id": "wai-quantum://schemas/convert_program/1", + "type": "object", + "description": "What convert_program answers.", + "anyOf": [ + { + "anyOf": [ + { + "type": "object", + "description": "A program carried from one format to another (OpenQASM 3, QIR as LLVM text, jeff as its Cap'n Proto message) and checked (engine::formats::convert_json): the program written, the program as read in OpenQASM 3, the readers' and writer's notes with their codes, what was read and written, and the check that holds the output to the input.", + "required": [ + "ok", + "format", + "from", + "to", + "qasm", + "notes", + "read", + "written", + "check" + ], + "properties": { + "ok": { + "const": true + }, + "format": { + "const": "wai.quantum.convert/1" + }, + "from": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "to": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program written, for a text format (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program written, for a binary format (jeff), as standard base64." + }, + "qasm": { + "type": "string", + "description": "The program as read, as OpenQASM 3: what every other tool takes." + }, + "notes": { + "type": "array", + "items": { + "type": "object", + "description": "One of the reader's diagnostics: its code, what happens to the construct it is about, its message (without the line or the code), and where it stands.", + "required": [ + "code", + "outcome", + "message" + ], + "properties": { + "code": { + "type": "string" + }, + "outcome": { + "enum": [ + "refused", + "lowered", + "carried", + "preserved", + "warned" + ] + }, + "message": { + "type": "string" + }, + "line": { + "type": "integer", + "minimum": 1 + }, + "column": { + "type": "integer", + "minimum": 1 + }, + "length": { + "type": "integer", + "minimum": 0 + } + } + }, + "description": "Each warning and note, with its code (Q7xx for the formats) and, for QIR, its line." + }, + "read": { + "type": "object", + "required": [ + "format" + ], + "description": "What was read: the format, its version, a QIR module's profile, entry point, qubits, results and declared capabilities, a jeff module's tool, entry point, qubits and bits." + }, + "written": { + "type": "object", + "required": [ + "format" + ], + "description": "What was written: the format, its version, and QIR's profile." + }, + "check": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "required": [ + "what", + "verdict" + ], + "properties": { + "what": { + "type": "string", + "description": "What was held to what." + }, + "verdict": { + "type": "object", + "required": [ + "verdict", + "how", + "why" + ], + "properties": { + "verdict": { + "enum": [ + "equivalent", + "different", + "unchecked" + ] + }, + "how": { + "enum": [ + "tableau", + "exact", + "rewrites", + "random", + "state", + "shape", + "steps", + "distribution", + "identity", + "none" + ], + "description": "The equivalence ladder's check (tableau, exact and rewrites are proofs; random is evidence); steps: the same steps one for one; distribution: every outcome's probability the same (evidence); identity: OpenQASM passed through; none: unchecked." + }, + "why": { + "type": "string" + }, + "global_phase": { + "type": [ + "number", + "null" + ] + } + } + } + } + } + ], + "description": "The output read back and held to the program read (or, for a program read into OpenQASM, written back to its format and read again), or null when no check was asked." + } + } + }, + { + "type": "object", + "description": "A refusal as data: the message, every code in it (the last is the code), its outcome and meaning as the code's table gives them. A host that refuses for its own reasons gives the error alone.", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "codes": { + "type": "array", + "items": { + "type": "string" + } + }, + "outcome": { + "enum": [ + "refused", + "retry", + "failed", + "lowered", + "carried", + "preserved", + "warned", + "dropped", + "defaulted" + ] + }, + "meaning": { + "type": "string" + } + } + } + ] + }, + { + "type": "object", + "description": "A refusal as data: the message, every code in it (the last is the code), its outcome and meaning as the code's table gives them. A host that refuses for its own reasons gives the error alone.", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "codes": { + "type": "array", + "items": { + "type": "string" + } + }, + "outcome": { + "enum": [ + "refused", + "retry", + "failed", + "lowered", + "carried", + "preserved", + "warned", + "dropped", + "defaulted" + ] + }, + "meaning": { + "type": "string" + } + } + } + ] + } + }, { "name": "improve_qasm", "title": "Improve a quantum program", @@ -37090,9 +37643,20 @@ "inputSchema": { "type": "object", "required": [ - "qasm", "target" ], + "anyOf": [ + { + "required": [ + "qasm" + ] + }, + { + "required": [ + "source" + ] + } + ], "properties": { "qasm": { "type": "string", @@ -37139,118 +37703,29 @@ } } }, - "pulse": { + "source": { "type": "object", - "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", - "additionalProperties": false, + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", "properties": { - "dt_ns": { - "type": "number", - "exclusiveMinimum": 0, - "maximum": 1000, - "description": "The sample period, ns (default 0.5)." - }, - "levels": { - "type": "integer", + "format": { + "type": "string", "enum": [ - 2, - 3 + "qasm", + "qir", + "jeff" ], - "description": "Levels kept per qubit (default 2; 3 shows leakage)." + "description": "Its format." }, - "tolerance": { - "type": "number", - "exclusiveMinimum": 0, - "exclusiveMaximum": 1, - "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + "program": { + "type": "string", + "description": "The program's text: QIR (LLVM text) or OpenQASM." }, - "substeps": { - "type": "integer", - "minimum": 1, - "maximum": 4096, - "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." - }, - "qubits": { - "type": "object", - "description": "Each qubit's parameters by its index, over the target's and the defaults.", - "additionalProperties": { - "type": "object", - "additionalProperties": false, - "properties": { - "frequency_ghz": { - "type": "number", - "description": "Its frequency, GHz." - }, - "rabi_mhz": { - "type": "number", - "description": "Its Rabi frequency at full amplitude, MHz." - }, - "anharmonicity_mhz": { - "type": "number", - "description": "Its anharmonicity, MHz." - }, - "t1_us": { - "type": "number", - "description": "Its relaxation time, µs." - }, - "t2_us": { - "type": "number", - "description": "Its coherence time, µs (at most 2·T1)." - } - } - } - }, - "couplings": { - "type": "array", - "description": "Static ZZ couplings.", - "items": { - "type": "object", - "required": [ - "qubits", - "zz_khz" - ], - "additionalProperties": false, - "properties": { - "qubits": { - "type": "array", - "items": { - "type": "integer", - "minimum": 0 - }, - "minItems": 2, - "maxItems": 2 - }, - "zz_khz": { - "type": "number", - "description": "The coupling's rate, kHz." - } - } - } - }, - "ports": { - "type": "object", - "description": "Ports by name: what each is for and the qubit it reaches.", - "additionalProperties": { - "type": "object", - "required": [ - "kind", - "qubit" - ], - "additionalProperties": false, - "properties": { - "kind": { - "enum": [ - "drive", - "measure", - "acquire" - ] - }, - "qubit": { - "type": "integer", - "minimum": 0 - } - } - } + "program_base64": { + "type": "string", + "description": "The program's bytes as base64: jeff." } } }, @@ -37532,6 +38007,121 @@ } } ] + }, + "pulse": { + "type": "object", + "description": "A pulse run's options (method \"pulse\"): the sample period, the levels kept, the tolerance on the stated error, the steps a sample is divided into, each qubit's parameters, static couplings, and ports beyond the names d0, m0, a0.", + "additionalProperties": false, + "properties": { + "dt_ns": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1000, + "description": "The sample period, ns (default 0.5)." + }, + "levels": { + "type": "integer", + "enum": [ + 2, + 3 + ], + "description": "Levels kept per qubit (default 2; 3 shows leakage)." + }, + "tolerance": { + "type": "number", + "exclusiveMinimum": 0, + "exclusiveMaximum": 1, + "description": "The most the stated error may be (default 1e-6): a run whose bound and rounding estimate pass it is refused, naming the steps that would meet it." + }, + "substeps": { + "type": "integer", + "minimum": 1, + "maximum": 4096, + "description": "Steps per sample (default: chosen so each step times the generator's norm is at most 0.5)." + }, + "qubits": { + "type": "object", + "description": "Each qubit's parameters by its index, over the target's and the defaults.", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "frequency_ghz": { + "type": "number", + "description": "Its frequency, GHz." + }, + "rabi_mhz": { + "type": "number", + "description": "Its Rabi frequency at full amplitude, MHz." + }, + "anharmonicity_mhz": { + "type": "number", + "description": "Its anharmonicity, MHz." + }, + "t1_us": { + "type": "number", + "description": "Its relaxation time, µs." + }, + "t2_us": { + "type": "number", + "description": "Its coherence time, µs (at most 2·T1)." + } + } + } + }, + "couplings": { + "type": "array", + "description": "Static ZZ couplings.", + "items": { + "type": "object", + "required": [ + "qubits", + "zz_khz" + ], + "additionalProperties": false, + "properties": { + "qubits": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + }, + "minItems": 2, + "maxItems": 2 + }, + "zz_khz": { + "type": "number", + "description": "The coupling's rate, kHz." + } + } + } + }, + "ports": { + "type": "object", + "description": "Ports by name: what each is for and the qubit it reaches.", + "additionalProperties": { + "type": "object", + "required": [ + "kind", + "qubit" + ], + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "drive", + "measure", + "acquire" + ] + }, + "qubit": { + "type": "integer", + "minimum": 0 + } + } + } + } + } } } }, @@ -37611,6 +38201,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -37618,6 +38213,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -40437,6 +41056,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -40444,6 +41068,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, @@ -43443,6 +44091,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -43450,6 +44103,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, diff --git a/wai/wai-quantum-cli/locales/zh-CN.json b/wai/wai-quantum-cli/locales/zh-CN.json index d97c1f501..4c2311b45 100644 --- a/wai/wai-quantum-cli/locales/zh-CN.json +++ b/wai/wai-quantum-cli/locales/zh-CN.json @@ -37,6 +37,7 @@ "run": { "source": "Run a program here, planned as the job service plans it, and sign the result", "text": "在本机运行程序,按作业服务的方式规划,并为结果签名" }, "plan": { "source": "Show the method a program would run on and why, without running it", "text": "显示程序将在哪种方法上运行及其原因,而不运行它" }, "instance": { "source": "Write a generator out at its `--input` values, as plain OpenQASM", "text": "按 `--input` 给出的取值,把生成器写成普通的 OpenQASM" }, + "convert": { "source": "Carry a program between formats (OpenQASM 3, QIR as LLVM text, jeff), every conversion checked: what is written is read back and held to the program by the equivalence ladder; notes and the check go to standard error (exit 1 refused, 2 the check found it different)", "text": "在格式之间转换程序(OpenQASM 3、以 LLVM 文本表示的 QIR、jeff),每次转换都经过检查:写出的结果会被读回,并由等价判定阶梯与原程序比对;说明和检查结果输出到标准错误(退出状态 1 表示被拒绝,2 表示检查发现两者不同)" }, "lint": { "source": "Check a program and print each problem with its line", "text": "检查程序,并打印每个问题及其所在的行" }, "draw": { "source": "Draw a program's circuit: as text, or as an SVG image, LaTeX (plain TikZ) or words", "text": "绘制程序的线路:以文本、SVG 图像、LaTeX(纯 TikZ)或文字描述的形式" }, "debug": { "source": "Step through a program on the engine's exact methods and look into the state at a position or before a line, or list every branch at its measurements in the middle with its weight", "text": "在引擎的精确方法上单步执行程序,查看某个位置或某一行之前的状态,或列出中途测量处的每个分支及其权重" }, diff --git a/wai/wai-quantum-cli/skills/wai-quantum/reference/tools.md b/wai/wai-quantum-cli/skills/wai-quantum/reference/tools.md index 7e479769b..0681b76dd 100644 --- a/wai/wai-quantum-cli/skills/wai-quantum/reference/tools.md +++ b/wai/wai-quantum-cli/skills/wai-quantum/reference/tools.md @@ -10,6 +10,7 @@ Generated by `wai-quantum docs skill` from the servers' own lists; `wai-quantum - `repair_qasm`: Repair a program on its flagged lines - `critique_qasm`: Critique an OpenQASM program - `check_equivalent`: Check two programs are the same circuit +- `convert_program`: Convert a program between formats - `improve_qasm`: Improve an OpenQASM program - `classical_shortcuts`: Find a program's classical shortcuts - `compile_qasm`: Compile a program, every pass checked @@ -57,6 +58,7 @@ Generated by `wai-quantum docs skill` from the servers' own lists; `wai-quantum - `verify_quantum_receipt`: Verify a result's receipt - `critique_qasm`: Critique a quantum program - `check_equivalent`: Check two quantum programs are the same circuit +- `convert_program`: Convert a program between formats - `improve_qasm`: Improve a quantum program - `classical_shortcuts`: Find a quantum program's classical shortcuts - `compile_qasm`: Compile a quantum program, every pass checked diff --git a/wai/wai-quantum-cli/smoke.sh b/wai/wai-quantum-cli/smoke.sh index 5f94c4401..ec8fc02a0 100644 --- a/wai/wai-quantum-cli/smoke.sh +++ b/wai/wai-quantum-cli/smoke.sh @@ -126,6 +126,22 @@ if "$W" equivalent "$tmp/twice.qasm" "$tmp/ghz.qasm" > "$tmp/differ"; then fail has "$tmp/differ" "^different: one has 2 qubits and the other 3" echo "critique, improve, equivalent: ok" +# Other formats: OpenQASM written as QIR and jeff, each read back and checked, +# every verb reading them, and a module its profile forbids refused on its line. +"$W" convert "$tmp/twice.qasm" --to qir > "$tmp/twice.ll" 2> "$tmp/convert" +has "$tmp/convert" "^check: equivalent (tableau)" +has "$tmp/twice.ll" '"qir_profiles"="base_profile"' +"$W" convert "$tmp/twice.ll" --to jeff -o "$tmp/twice.jeff" 2> "$tmp/convert" +has "$tmp/convert" "^check: equivalent (tableau)" +"$W" equivalent "$tmp/twice.jeff" "$tmp/twice.qasm" > "$tmp/same" 2>/dev/null +has "$tmp/same" "^equivalent" +"$W" run "$tmp/twice.ll" --shots 64 --json > "$tmp/fromqir.json" +"$W" verify "$tmp/fromqir.json" > /dev/null || fail "a run of a QIR module did not verify" +printf 'define i64 @main() #0 {\nentry:\n call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n call void @__quantum__qis__x__body(ptr null)\n ret i64 0\n}\nattributes #0 = { "entry_point" "qir_profiles"="base_profile" "required_num_qubits"="1" "required_num_results"="1" }\n' > "$tmp/forbidden.ll" +if "$W" convert "$tmp/forbidden.ll" --to qasm > /dev/null 2> "$tmp/refused"; then fail "a gate after a measurement under the base profile was read"; fi +has "$tmp/refused" "line 4: the base profile forbids a gate on qubit 0 after its measurement \\[Q704\\]" +echo "convert: QIR and jeff, written, read back and checked; a profile's refusal on its line: ok" + "$W" shortcuts "$tmp/ghz.qasm" > "$tmp/shortcuts" has "$tmp/shortcuts" "^Classically easy at any width" printf 'OPENQASM 3.0;\nqubit[8] q;\nh q;\ncz q[0], q[1];\ncz q[1], q[2];\ncz q[2], q[3];\ncz q[3], q[4];\ncz q[4], q[5];\ncz q[5], q[6];\ncz q[6], q[7];\ncz q[7], q[0];\nrz(0.3) q;\n' > "$tmp/ring.qasm" diff --git a/wai/wai-quantum-cli/src/main.rs b/wai/wai-quantum-cli/src/main.rs index b862f319b..606172909 100644 --- a/wai/wai-quantum-cli/src/main.rs +++ b/wai/wai-quantum-cli/src/main.rs @@ -24,6 +24,8 @@ //! wai-quantum repair prog.qasm > fixed.qasm # the mechanical fixes, on the lines the diagnostics flag alone //! wai-quantum run ghz.qasm --input n=40 # a generator, read at n = 40 //! wai-quantum instance qft.qasm --input n=5 # a generator written out as plain OpenQASM +//! wai-quantum convert prog.qasm --to qir # QIR 2.0 (or jeff, or OpenQASM 3), the conversion checked +//! wai-quantum run prog.ll --shots 1000 # every verb reads QIR (.ll) and jeff (.jeff); --from names one //! wai-quantum discover new ghz.json # the workbench: an archive for a specification //! wai-quantum discover search ghz.json # cheaper generators, each checked at every size //! wai-quantum history blame prog.qasm # whose hand each line and gate is in @@ -129,6 +131,9 @@ struct Cli { /// The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English #[arg(long, global = true, value_name = "LOCALE")] lang: Option, + /// The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise + #[arg(long, global = true, value_name = "FORMAT", value_parser = format_arg)] + from: Option, #[command(subcommand)] command: Command, } @@ -160,6 +165,26 @@ enum Command { /// The program; `-` or nothing reads standard input file: Option, }, + /// Carry a program between formats (OpenQASM 3, QIR as LLVM text, jeff), every conversion checked: what is written is read back and held to the program by the equivalence ladder; notes and the check go to standard error (exit 1 refused, 2 the check found it different) + Convert { + /// The program; `-` or nothing reads standard input; its format is --from, else its extension, else what its bytes look like + file: Option, + /// The format to write: qasm, qir or jeff + #[arg(long, value_name = "FORMAT", value_parser = format_arg)] + to: wai_quantum::engine::formats::Format, + /// QIR's profile, base or adaptive: by default the base profile when the program's measurements all come at the end, else the adaptive one + #[arg(long)] + profile: Option, + /// Write the program to this file (jeff is binary: give a file, or --json for its base64) + #[arg(short = 'o', long, value_name = "FILE")] + output: Option, + /// Print the conversion as JSON: the program (base64 for jeff), the program as OpenQASM, the notes, what was read and written, and the check + #[arg(long)] + json: bool, + /// Write it without the check + #[arg(long)] + no_check: bool, + }, /// Check a program and print each problem with its line Lint { /// The program; `-` or nothing reads standard input @@ -559,7 +584,52 @@ fn die(msg: &str) -> ! { /// (a program with integer `input`s) is written out at the `--input` values, /// so every command reads one circuit of its family. fn read_input(file: Option<&Path>) -> Result { - at_inputs(read_text(file)?, &INPUTS.get().cloned().unwrap_or_default()) + at_inputs(read_program(file)?, &INPUTS.get().cloned().unwrap_or_default()) +} + +/// The format `--from` gives, for every program read. +static FROM: std::sync::OnceLock> = std::sync::OnceLock::new(); + +fn format_arg(s: &str) -> Result { + wai_quantum::engine::formats::Format::parse(s).ok_or_else(|| format!("{s}: the formats are qasm, qir and jeff")) +} + +/// The bytes of `file`, or standard input for `-` or none. +fn read_bytes(file: Option<&Path>) -> Result, String> { + match file { + Some(p) if p != Path::new("-") => std::fs::read(p).map_err(|e| format!("{}: {e}", p.display())), + _ => { + use std::io::Read as _; + let mut b = Vec::new(); + std::io::stdin().read_to_end(&mut b).map_err(|e| format!("standard input: {e}"))?; + Ok(b) + } + } +} + +/// The format a program is in: `--from`, else its file's extension (`.ll` +/// and `.qir` are QIR, `.jeff` is jeff), else OpenQASM. +fn format_of(file: Option<&Path>) -> wai_quantum::engine::formats::Format { + use wai_quantum::engine::formats::Format; + if let Some(Some(f)) = FROM.get() { + return *f; + } + file.and_then(|p| Format::from_extension(&p.to_string_lossy())).unwrap_or(Format::Qasm) +} + +/// The program in `file` as OpenQASM: one in another format is read to it, +/// its notes on standard error. +fn read_program(file: Option<&Path>) -> Result { + use wai_quantum::engine::formats::{self, Format}; + let f = format_of(file); + if f == Format::Qasm { + return read_text(file); + } + let r = formats::read(f, &read_bytes(file)?).map_err(|d| d.to_string())?; + for n in &r.notes { + eprintln!("wai-quantum: {}: {n}", f.name()); + } + Ok(r.qasm) } /// `text` as one circuit: a generator written out at `inputs`, any other @@ -588,7 +658,7 @@ fn read_text(file: Option<&Path>) -> Result { /// request carries the history's account of the version the file holds; /// with `--intent`, what the program must do. fn job_request(job: &Job) -> Result { - let text = read_text(job.file.as_deref())?; + let text = read_program(job.file.as_deref())?; let inputs = INPUTS.get().cloned().unwrap_or_default(); // A sweep sends the program with its inputs, which the sweep gives values. if !job.sweep.is_empty() && !inputs.is_empty() { @@ -599,7 +669,7 @@ fn job_request(job: &Job) -> Result { if !job.programs.is_empty() { let mut all = vec![req["qasm"].take()]; for p in &job.programs { - all.push(json!(at_inputs(read_text(Some(p))?, &inputs)?)); + all.push(json!(at_inputs(read_program(Some(p))?, &inputs)?)); } req.as_object_mut().map(|o| o.remove("qasm")); req["programs"] = Value::Array(all); @@ -940,6 +1010,54 @@ fn print_dry_run(p: &Value) { println!("you {} active, {} submissions left in this window", p["client"]["active_jobs"], p["client"]["submissions_left"]); } +/// `convert`: the program in `file` written as `to`, to `output` or standard +/// output, its notes and the check on standard error (or all of it as JSON). +fn convert(file: Option<&Path>, to: wai_quantum::engine::formats::Format, profile: Option<&str>, output: Option<&Path>, as_json: bool, no_check: bool) { + use std::io::{IsTerminal, Write as _}; + use wai_quantum::engine::formats::{self, Format, Profile}; + let bytes = read_bytes(file).unwrap_or_else(|e| die(&e)); + let from = match FROM.get() { + Some(Some(f)) => *f, + _ => file.and_then(|p| Format::from_extension(&p.to_string_lossy())).unwrap_or_else(|| Format::sniff(&bytes)), + }; + let profile = profile.map(|p| Profile::parse(p).unwrap_or_else(|| die(&format!("{p}: the QIR profiles are base and adaptive")))); + let c = formats::convert(from, to, &bytes, profile, !no_check).unwrap_or_else(|d| die(&d.to_string())); + let verdict = c.check["verdict"]["verdict"].as_str().unwrap_or(""); + if as_json { + let mut v = json!({ + "ok": true, "format": formats::FORMAT, "from": from.name(), "to": to.name(), "qasm": c.qasm, + "notes": c.notes.iter().map(|d| d.to_json()).collect::>(), "read": c.read, "written": c.written, "check": c.check, + }); + if to.is_binary() { + v["program_base64"] = json!(formats::encode_base64(&c.output)); + } else { + v["program"] = json!(String::from_utf8_lossy(&c.output)); + } + if let Some(o) = output { + std::fs::write(o, &c.output).unwrap_or_else(|e| die(&format!("{}: {e}", o.display()))); + } + println!("{v}"); + } else { + for n in &c.notes { + eprintln!("note: {n}"); + } + if !c.check.is_null() { + eprintln!("check: {} ({}): {}", verdict, c.check["verdict"]["how"].as_str().unwrap_or(""), c.check["what"].as_str().unwrap_or("")); + } + match output { + Some(o) => std::fs::write(o, &c.output).unwrap_or_else(|e| die(&format!("{}: {e}", o.display()))), + None if to.is_binary() && std::io::stdout().is_terminal() => die("jeff is binary: give -o FILE, or --json for its base64"), + None => { + let mut out = std::io::stdout(); + out.write_all(&c.output).and_then(|_| out.flush()).unwrap_or_else(|e| die(&format!("standard output: {e}"))); + } + } + } + if verdict == "different" { + std::process::exit(2); + } +} + fn main() { // The help a parse prints is in the person's language, so the language is read first. let args: Vec = std::env::args().collect(); @@ -947,6 +1065,7 @@ fn main() { locale::set(loc); let cli = Cli::from_arg_matches(&locale::localize(Cli::command(), loc).get_matches_from(&args)).unwrap_or_else(|e| e.exit()); let _ = INPUTS.set(cli.inputs.iter().cloned().collect()); + let _ = FROM.set(cli.from); match cli.command { #[cfg(feature = "providers")] Command::Run { job, out, key, provider } if provider.provider.is_some() => providers::run(&job, &out, &key, &provider), @@ -1027,6 +1146,7 @@ fn main() { Command::Instance { file } => { print!("{}", read_input(file.as_deref()).unwrap_or_else(|e| die(&e))); } + Command::Convert { file, to, profile, output, json, no_check } => convert(file.as_deref(), to, profile.as_deref(), output.as_deref(), json, no_check), Command::Lint { file, json } => { let name = file.as_deref().filter(|p| *p != Path::new("-")).map(|p| p.display().to_string()).unwrap_or_else(|| "".into()); let src = read_input(file.as_deref()).unwrap_or_else(|e| die(&e)); diff --git a/wai/wai-quantum-cli/src/mcp.rs b/wai/wai-quantum-cli/src/mcp.rs index dac5659e7..a77e9dd54 100644 --- a/wai/wai-quantum-cli/src/mcp.rs +++ b/wai/wai-quantum-cli/src/mcp.rs @@ -67,6 +67,7 @@ fn output_of(tool: &str) -> Option<&'static str> { "lint_qasm" => "tools.v1.json#/$defs/lint", "critique_qasm" => "tools.v1.json#/$defs/critique", "check_equivalent" => "tools.v1.json#/$defs/equivalence", + "convert_program" => "tools.v1.json#/$defs/convert", "improve_qasm" => "tools.v1.json#/$defs/improve", "classical_shortcuts" => "tools.v1.json#/$defs/shortcuts", "draw_circuit" => "tools.v1.json#/$defs/draw", @@ -99,8 +100,9 @@ fn output_of(tool: &str) -> Option<&'static str> { fn job_schema() -> Value { json!({ "type": "object", - "required": ["qasm"], + "anyOf": wai_quantum::engine::formats::program_required(), "properties": { + "source": wai_quantum::engine::formats::source_schema(), "qasm": { "type": "string", "description": "The program, OpenQASM 3 or 2: registers and broadcast over them, the standard gates, ctrl/negctrl modifiers, gate definitions, measurement and reset anywhere, if on measured bits, and generators (integer input, const and for loops, one program for a family of circuits; give the inputs' values in \"inputs\"). while, inv and pow are refused with their line." }, "inputs": inputs_schema(), "shots": { "type": "integer", "minimum": 0, "description": "Shots to sample; 1024 when nothing else is asked." }, @@ -267,6 +269,7 @@ fn core_tools() -> Value { "description": "Whether two programs are the same circuit up to a global phase, and how the engine knows: equal tableaus for Clifford circuits at any size, every basis state to 8 qubits, checked rewrites of one followed by the other's inverse to nothing (a proof at any width), random product states to 22 qubits (evidence, not proof), or unchecked with the reason. Its `verdict` is that decision; `verification` is the level it reached, as every tool's verdict states it.", "inputSchema": { "type": "object", "required": ["a", "b"], "properties": { "a": { "type": "string", "description": "The first program." }, "b": { "type": "string", "description": "The second program." }, "inputs": inputs_schema() } }, }, + wai_quantum::engine::formats::tool(), { "name": "improve_qasm", "title": "Improve an OpenQASM program", @@ -471,10 +474,24 @@ fn call(name: &str, args: &Value, memory_gb: u64, key: &SigningKey, client: &Cli let result = wai_quantum::engine::pulse::call(op, args)?; return Ok(wai_quantum::engine::pulse::seal(args, result, key)); } + // A run's program may come in another format, read to its qasm first, as + // the job service reads it. + let expanded; + let args = match name { + "run_quantum_circuit" | "plan_quantum_circuit" | "run_on_target" => match wai_quantum::engine::formats::expand(args)? { + Some(e) => { + expanded = e; + &expanded + } + None => args, + }, + _ => args, + }; // A tool that reads a generator at its inputs itself takes them as given. let raw = args; // The debugger reads a generator at its inputs itself, so its lines are the generator's. match name { + "convert_program" => return wai_quantum::engine::formats::convert_json(raw), "debug_step" => return wai_quantum::engine::debug::call("step", raw), "debug_branches" => return wai_quantum::engine::debug::call("branches", raw), _ => {} @@ -840,6 +857,30 @@ mod tests { SigningKey::from_bytes(&[5; 32]) } + /// A run takes its program in another format as the job service's run + /// tools take it: the same `source`, read to its qasm first, and the same + /// refusal, the reader's code before the job layer's. + #[test] + fn a_run_takes_its_program_as_a_source_as_the_service_does() { + let listed = tools(); + for name in ["run_quantum_circuit", "plan_quantum_circuit", "run_on_target"] { + let t = listed.as_array().unwrap().iter().find(|t| t["name"] == name).unwrap(); + assert_eq!(t["inputSchema"]["properties"]["source"], wai_quantum::engine::formats::source_schema(), "{name}"); + } + let bell = "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n"; + let qir = wai_quantum::engine::formats::convert_json(&json!({ "program": bell, "to": "qir" })).unwrap(); + let call_run = |args: Value| handle(&json!({ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "run_quantum_circuit", "arguments": args } }), 1, &key(), &mut Client::default()).unwrap()["result"].clone(); + let r = call_run(json!({ "source": { "format": "qir", "program": qir["program"] }, "shots": 64 })); + assert_eq!(r["isError"], false, "{r}"); + let counts = r["structuredContent"]["result"]["counts"].as_object().unwrap(); + assert!(counts.keys().all(|k| k == "00" || k == "11"), "{r}"); + assert_eq!(r["structuredContent"]["request"]["source"]["format"], "qir", "the request keeps its source"); + let bad = call_run(json!({ "source": { "format": "qir", "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n" } })); + assert_eq!((bad["isError"].as_bool(), bad["structuredContent"]["codes"].clone()), (Some(true), json!(["Q706", "J001"])), "{bad}"); + let both = call_run(json!({ "qasm": bell, "source": { "format": "qir", "program": qir["program"] } })); + assert_eq!(both["structuredContent"]["code"], "J003", "{both}"); + } + #[test] fn an_agent_initializes_lists_the_tools_and_runs_a_circuit() { let init = handle(&json!({ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": PROTOCOL } }), 1, &key(), &mut Client::default()).unwrap(); @@ -855,7 +896,7 @@ mod tests { let list = handle(&json!({ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }), 1, &key(), &mut Client::default()).unwrap(); let names: Vec<&str> = list["result"]["tools"].as_array().unwrap().iter().map(|t| t["name"].as_str().unwrap()).collect(); #[allow(unused_mut)] - let mut want = vec!["run_quantum_circuit", "plan_quantum_circuit", "lint_qasm", "repair_qasm", "critique_qasm", "check_equivalent", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "draw_circuit", "debug_step", "debug_branches", "verify_quantum_result", "discover_evaluate", "discover_submit", "discover_archive", "novelty_qasm", "discover_novelty", "rewrite_qasm", "converge_cohort", "converge_history", "study_measure", "study_cohort", "taste_rank", "taste_next", "history_commit", "history_changes", "history_blame", "history_log", "list_methods", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]; + let mut want = vec!["run_quantum_circuit", "plan_quantum_circuit", "lint_qasm", "repair_qasm", "critique_qasm", "check_equivalent", "convert_program", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "draw_circuit", "debug_step", "debug_branches", "verify_quantum_result", "discover_evaluate", "discover_submit", "discover_archive", "novelty_qasm", "discover_novelty", "rewrite_qasm", "converge_cohort", "converge_history", "study_measure", "study_cohort", "taste_rank", "taste_next", "history_commit", "history_changes", "history_blame", "history_log", "list_methods", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]; #[cfg(feature = "providers")] want.extend(["providers_list", "provider_estimate", "run_on_provider"]); assert_eq!(names, want); @@ -1304,6 +1345,12 @@ mod tests { call_tool("critique_qasm", json!({ "qasm": draft }), false, &mut called, &mut hold); call_tool("check_equivalent", json!({ "a": draft, "b": "OPENQASM 3.0;\nqubit[2] q;\ncx q[0], q[1];\nt q[1];\n" }), false, &mut called, &mut hold); call_tool("improve_qasm", json!({ "qasm": draft }), false, &mut called, &mut hold); + let qir = call_tool("convert_program", json!({ "program": draft, "to": "qir" }), false, &mut called, &mut hold); + call_tool("run_quantum_circuit", json!({ "source": { "format": "qir", "program": qir["program"] }, "shots": 20 }), false, &mut called, &mut hold); + call_tool("run_quantum_circuit", json!({ "source": { "format": "qir", "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n" } }), true, &mut called, &mut hold); + let jeff = call_tool("convert_program", json!({ "program": qir["program"], "to": "jeff" }), false, &mut called, &mut hold); + call_tool("convert_program", json!({ "program_base64": jeff["program_base64"], "to": "qasm" }), false, &mut called, &mut hold); + call_tool("convert_program", json!({ "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n", "from": "qir", "to": "qasm" }), true, &mut called, &mut hold); call_tool("classical_shortcuts", json!({ "qasm": draft }), false, &mut called, &mut hold); call_tool("compile_qasm", json!({ "qasm": "OPENQASM 3.0;\nqubit[2] q;\nh q[0];\nrz(0.3) q[0];\ncx q[0], q[1];\n", "epsilon": 1e-3 }), false, &mut called, &mut hold); call_tool("compile_qasm", json!({ "qasm": draft, "basis": "standard", "level": 2, "stages": true }), false, &mut called, &mut hold); diff --git a/wai/wai-quantum-cli/src/targets.rs b/wai/wai-quantum-cli/src/targets.rs index 743f7e8c2..c01aee03d 100644 --- a/wai/wai-quantum-cli/src/targets.rs +++ b/wai/wai-quantum-cli/src/targets.rs @@ -242,8 +242,9 @@ pub fn main(cmd: Cmd) { /// The agent's tools for targets ([`crate::mcp::tools`]). pub fn mcp_tools(job_schema: Value) -> Vec { let mut run = job_schema; - if let Some(r) = run["required"].as_array_mut() { - r.push(json!("target")); + match run["required"].as_array_mut() { + Some(r) => r.push(json!("target")), + None => run["required"] = json!(["target"]), } vec![ json!({ diff --git a/wai/wai-quantum-desktop/src/main.rs b/wai/wai-quantum-desktop/src/main.rs index 074aa754c..992053657 100644 --- a/wai/wai-quantum-desktop/src/main.rs +++ b/wai/wai-quantum-desktop/src/main.rs @@ -214,6 +214,18 @@ fn equivalent(a: String, b: String) -> String { equiv::equivalent(&a, &b).to_string() } +/// A program carried between formats (OpenQASM 3, QIR, jeff as base64), +/// checked; a refusal as data, `{error, code, codes, outcome, meaning}`. +#[tauri::command] +fn convert(request: String) -> String { + let answer = serde_json::from_str::(&request).map_err(|e| format!("the request is not JSON: {e}")).and_then(|r| wai_quantum::engine::formats::convert_json(&r)); + answer.map(|v| v.to_string()).unwrap_or_else(|e| { + let mut r = wai_quantum::engine::refusal::refusal_json(&e); + r["error"] = serde_json::json!(e); + r.to_string() + }) +} + #[tauri::command] fn variants(qasm: String) -> String { critique::variants(&qasm).to_string() @@ -964,7 +976,7 @@ fn main() { .plugin(tauri_plugin_updater::Builder::new().build()) .manage(Studio { key, job_bytes: job_bytes(), running: Arc::new(Mutex::new(None)), cancelled: Arc::new(Mutex::new(false)) }) .manage(PendingUpdate(Mutex::new(None))) - .invoke_handler(tauri::generate_handler![run_job, cancel, plan_job, methods, lint, critique, equivalent, variants, shortcuts, compile, verdict, repair, course, course_lesson, course_check, course_hint, estimate, estimate_verify, qec, pulse, draw, layout, debug, visual_edit, picture, verify, signer, save_job, save_program, chat, models, discover_start, discover_submit, discover_search, discover_evaluate, discover_novelty, instantiate, history, locks, novelty, rewrite, taste, converge, study, providers_list, provider_estimate, run_on_provider, targets, target, target_place, answer, telemetry, update_check, update_install]) + .invoke_handler(tauri::generate_handler![run_job, cancel, plan_job, methods, lint, critique, equivalent, convert, variants, shortcuts, compile, verdict, repair, course, course_lesson, course_check, course_hint, estimate, estimate_verify, qec, pulse, draw, layout, debug, visual_edit, picture, verify, signer, save_job, save_program, chat, models, discover_start, discover_submit, discover_search, discover_evaluate, discover_novelty, instantiate, history, locks, novelty, rewrite, taste, converge, study, providers_list, provider_estimate, run_on_provider, targets, target, target_place, answer, telemetry, update_check, update_install]) .run(tauri::generate_context!()) .expect("the window opens"); } @@ -1254,6 +1266,17 @@ mod tests { assert!(s["summary"].as_str().unwrap().starts_with("Classically easy"), "{s}"); } + /// A QIR module opened in the app reads as OpenQASM 3, checked; one the + /// reader does not take is refused as data with its code. + #[test] + fn a_program_in_another_format_opens_in_the_app() { + let qir: Value = serde_json::from_str(&convert(json!({ "program": "qubit[2] q; h q[0]; cx q[0], q[1];", "to": "qir" }).to_string())).unwrap(); + let back: Value = serde_json::from_str(&convert(json!({ "program": qir["program"], "from": "qir", "to": "qasm" }).to_string())).unwrap(); + assert_eq!((back["from"].as_str(), back["check"]["verdict"]["verdict"].as_str()), (Some("qir"), Some("equivalent")), "{back}"); + let bad: Value = serde_json::from_str(&convert(json!({ "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n", "from": "qir", "to": "qasm" }).to_string())).unwrap(); + assert_eq!(bad["code"], "Q706", "{bad}"); + } + #[test] fn the_history_records_with_the_app_s_key_and_reads_back() { let key = SigningKey::from_bytes(&[3; 32]); diff --git a/wai/wai-quantum-service/README.md b/wai/wai-quantum-service/README.md index 5cb2203f6..3ab5c7984 100644 --- a/wai/wai-quantum-service/README.md +++ b/wai/wai-quantum-service/README.md @@ -19,7 +19,8 @@ A job is JSON: | field | meaning | |---|---| -| `qasm` | the program: OpenQASM 3, or OpenQASM 2 (required) | +| `qasm` | the program: OpenQASM 3, or OpenQASM 2 (required, unless `source` gives it) | +| `source` | the program in another format, in place of `qasm`: `{"format": "qir", "program": "…"}` (QIR as LLVM text) or `{"format": "jeff", "program_base64": "…"}`; read to the job's `qasm` before anything else reads the job, the request keeping its source, and a module that does not read refused with its code (`Q700` to `Q721`) and place | | `method` | `auto` (the default) or a method below | | `shots` | measurement shots (default 1024 when nothing else is asked for; at most 1,000,000) | | `seed` | the sampling seed (default 1); the same job and seed give the same counts | @@ -146,6 +147,7 @@ Counts are always seeded samples. | `POST /v1/verify` | `{"request": ..., "result": ...}`: whether the result's receipt seals that request and that result | | `POST /v1/critique` | `{"qasm"}`: the program's measures (gates by kind, two-qubit gates, depth, T count, whether it is Clifford, a modelled fidelity) and its smells, each with its lines | | `POST /v1/equivalent` | `{"a", "b"}`: whether two programs are the same circuit up to a global phase, and how that was decided | +| `POST /v1/convert` | `{"to", "program" \| "program_base64", "from"?, "profile"?, "check"?}`: the program carried between OpenQASM 3, QIR and jeff (as base64), with the program as OpenQASM 3, each note with its code, what was read and written, and the check (the output read back and held to the program by the equivalence ladder); a refusal is the reader's, with its code and place ([the formats page](../wai-quantum/docs/formats.md)) | | `POST /v1/improve` | `{"qasm"}`: the program with its smells fixed, each candidate with its measures and checked the same circuit | | `POST /v1/shortcuts` | `{"qasm"}`: each exact classical route to simulating the program, priced on it, and the cheapest for each question | | `POST /v1/compile` | `{"qasm", "epsilon", "level", "basis"}`: the program compiled through checked passes (to Clifford+T within `epsilon`, by default `1e-6`), with a certificate of every pass and the distance, sealed with the service's key in a job's form so `POST /v1/verify` checks it ([the compiler page](../wai-quantum/docs/compiler.md)) | @@ -194,7 +196,7 @@ The six checks answer at once, in the service's own process. Each counts against ## For agents -`POST /mcp` serves the service to an agent as Model Context Protocol tools: `run_quantum_circuit`, `run_quantum_set` (a set or a sweep), `dry_run_quantum_job` (the plan, running nothing), `get_quantum_job`, `verify_quantum_receipt`, `critique_qasm`, `check_equivalent`, `improve_qasm`, `classical_shortcuts`, `compile_qasm`, `estimate_resources`, `repair_qasm`, `quantum_service_info`, `qasm_diagnostics`, `list_targets`, `show_target`, `run_on_target`, `quantum_circuit_examples`, `qec_code`, `qec_decode`, `qec_threshold`, `pulse_schedule` and `pulse_check`. A run it makes asks for its verdict, so the request its receipt seals is the job's own `request`; every answer about a program carries its verdict, every refusal its codes, and every tool a versioned output schema. `verify_quantum_receipt` re-runs an estimate. It also serves the engine's resources and prompts, the same as the command line's own server (`wai-quantum mcp`): +`POST /mcp` serves the service to an agent as Model Context Protocol tools: `run_quantum_circuit`, `run_quantum_set` (a set or a sweep), `dry_run_quantum_job` (the plan, running nothing), `get_quantum_job`, `verify_quantum_receipt`, `critique_qasm`, `check_equivalent`, `convert_program`, `improve_qasm`, `classical_shortcuts`, `compile_qasm`, `estimate_resources`, `repair_qasm`, `quantum_service_info`, `qasm_diagnostics`, `list_targets`, `show_target`, `run_on_target`, `quantum_circuit_examples`, `qec_code`, `qec_decode`, `qec_threshold`, `pulse_schedule` and `pulse_check`. A run it makes asks for its verdict, so the request its receipt seals is the job's own `request`; every answer about a program carries its verdict, every refusal its codes, and every tool a versioned output schema. `verify_quantum_receipt` re-runs an estimate. It also serves the engine's resources and prompts, the same as the command line's own server (`wai-quantum mcp`): - resources: the language the engine reads (`wai-quantum://docs/language`), its diagnostics, the methods with their accuracy classes (their reach in a job's memory here), and worked examples, each with what it must do; - prompts: writing and verifying a circuit for an intent, explaining a result and its receipt, and repairing a program from its diagnostics. Each names this server's own tools. diff --git a/wai/wai-quantum-service/schemas/service-tools.v1.json b/wai/wai-quantum-service/schemas/service-tools.v1.json index 1c6227399..f629f3cf5 100644 --- a/wai/wai-quantum-service/schemas/service-tools.v1.json +++ b/wai/wai-quantum-service/schemas/service-tools.v1.json @@ -82,6 +82,16 @@ } ] }, + "convert": { + "anyOf": [ + { + "$ref": "https://git.transaction.science/Transaction-Science/open-standards/raw/branch/main/wai/wai-quantum/schemas/tools.v1.json#/$defs/convert" + }, + { + "$ref": "#/$defs/refused" + } + ] + }, "improve": { "anyOf": [ { diff --git a/wai/wai-quantum-service/smoke.sh b/wai/wai-quantum-service/smoke.sh index 950de0600..411adb66f 100644 --- a/wai/wai-quantum-service/smoke.sh +++ b/wai/wai-quantum-service/smoke.sh @@ -13,7 +13,28 @@ U=${U%/} tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT -post() { curl -sS --max-time 120 -X POST "$U$1" -H 'content-type: application/json' --data-binary "@$2"; } +# A client may submit `q` jobs in a window (RateLimit-Policy), and the script +# makes more than a default window's 30. So before each POST it reads where it +# stands (RateLimit: `r` left, `t` seconds until one more is) from GET /v1/health, +# which submits nothing, and with none left it waits for one. CI starts the +# service with a larger WAI_QSVC_CLIENT_BURST, so it never waits there; against +# production it takes longer, and checks the same things. A dry run counts as a +# submission and then answers whether a real one would be taken now, so before +# one the script waits for two (`pace 2`). +pace() { + local need=${1:-1} h r t + for _ in $(seq 1 80); do + h=$(curl -sS --max-time 30 -D - -o /dev/null "$U/v1/health" | tr -d '\r' | sed -n 's/^[Rr]ate[Ll]imit: //p') + r=$(printf '%s' "$h" | sed -n 's/.*r=\([0-9]*\).*/\1/p') + t=$(printf '%s' "$h" | sed -n 's/.*t=\([0-9]*\).*/\1/p') + if [ -z "$r" ] || [ "$r" -ge "$need" ]; then return 0; fi + echo "pace: $r of the $need submissions needed left in this client's window; waiting ${t:-10} s" >&2 + sleep "$(( ${t:-10} < 1 ? 1 : ${t:-10} ))" + done + echo "FAIL: no submission came free in this client's window" >&2 + exit 1 +} +post() { pace; curl -sS --max-time 120 -X POST "$U$1" -H 'content-type: application/json' --data-binary "@$2"; } py() { python3 -I -c "$@"; } # POST /v1/run, then follow a job still queued or running past the service's # wait through GET /v1/jobs/{id}, as a client would: on a loaded host a job @@ -219,8 +240,9 @@ print("examples and openapi: ok,", len(e), "examples,", len(o["paths"]), "paths" # An agent over the Model Context Protocol: initialize, list the tools, run # the first example, and check its receipt. The run states its verdict, so the # request it seals (the job's own `request`) asks for one. -mcp() { curl -sS --max-time 120 -X POST "$U/mcp" -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' --data-binary "$1"; } +mcp() { pace; curl -sS --max-time 120 -X POST "$U/mcp" -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' --data-binary "$1"; } mcp '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"1"}}}' > "$tmp/mcp-init.json" +pace code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 30 -X POST "$U/mcp" -H 'content-type: application/json' --data-binary '{"jsonrpc":"2.0","method":"notifications/initialized"}') mcp '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' > "$tmp/mcp-tools.json" py 'import json,sys @@ -241,16 +263,20 @@ req=json.load(open(sys.argv[2]))["examples"][0]["job"] assert job["request"]==dict(req, verdict=True) and job["verdict"]["level"]=="executed" and job["verdict"]==job["result"]["verdict"], job req=job["request"] print(json.dumps({"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"verify_quantum_receipt","arguments":{"request":req,"result":job["result"]}}}))' "$tmp/mcp-run.json" "$tmp/examples.json" > "$tmp/mcp-verify.json" +pace curl -sS --max-time 60 -X POST "$U/mcp" -H 'content-type: application/json' --data-binary "@$tmp/mcp-verify.json" > "$tmp/mcp-verified.json" py 'import json,sys v=json.load(open(sys.argv[1]))["result"]; assert v["structuredContent"]["valid"] is True, v print("mcp run + verify: ok,", json.load(open(sys.argv[2]))["result"]["content"][0]["text"].splitlines()[1])' "$tmp/mcp-verified.json" "$tmp/mcp-run.json" # A host that draws views: the session id that says so, the view's own tool kept from the model, and a page. +pace curl -sS -D "$tmp/mcp-views.h" --max-time 60 -X POST "$U/mcp" -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' \ --data-binary '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{"extensions":{"io.modelcontextprotocol/ui":{"mimeTypes":["text/html;profile=mcp-app"]}}}}}' > /dev/null sid=$(tr -d '\r' < "$tmp/mcp-views.h" | awk 'tolower($1)=="mcp-session-id:" {print $2}') case "$sid" in wq-views-*) ;; *) echo "no views session id: $sid"; exit 1 ;; esac +pace curl -sS --max-time 60 -X POST "$U/mcp" -H 'content-type: application/json' -H "mcp-session-id: $sid" --data-binary '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' > "$tmp/mcp-views-tools.json" +pace curl -sS --max-time 60 -X POST "$U/mcp" -H 'content-type: application/json' -H "mcp-session-id: $sid" --data-binary '{"jsonrpc":"2.0","id":3,"method":"resources/read","params":{"uri":"ui://wai-quantum/result"}}' > "$tmp/mcp-views-page.json" py 'import json,sys t={x["name"]: x for x in json.load(open(sys.argv[1]))["result"]["tools"]} @@ -328,6 +354,7 @@ print("device target: ok, placed on", p["layout"], "expected success", round(p[" # A refusal says why, what to write instead, and ends with its code. py 'import json; print(json.dumps({"qasm": "qubit[1] q; bit c; c = measure q; while (c) { x q; c = measure q; }"}))' > "$tmp/bad.json" +pace code=$(curl -sS -o "$tmp/bad.out" -w '%{http_code}' -X POST "$U/v1/jobs" -H 'content-type: application/json' --data-binary "@$tmp/bad.json") [ "$code" = 400 ] || { echo "a bad program got HTTP $code"; exit 1; } py 'import json,sys; d=json.load(open(sys.argv[1])); assert d["code"]=="Q310" and "@wai.max_iterations" in d["error"], d; print("refusal: ok,", d["error"])' "$tmp/bad.out" @@ -344,7 +371,16 @@ py 'import json,sys; c=json.load(open(sys.argv[1]))["candidates"][0]; assert c[" py 'import json; print(json.dumps({"a": "OPENQASM 3.0;\nqubit[2] q;\nh q[0];\nh q[0];\ncx q[0], q[1];\n", "b": "OPENQASM 3.0;\nqubit[2] q;\ncx q[0], q[1];\n"}))' > "$tmp/pair.json" post /v1/equivalent "$tmp/pair.json" > "$tmp/equivalent.out" py 'import json,sys; d=json.load(open(sys.argv[1])); assert d["verdict"]=="equivalent" and d["how"]=="tableau", d; print("equivalent: ok, by tableau")' "$tmp/equivalent.out" +# A program carried to QIR and back, each conversion checked, and a job +# that carries its program as QIR. +py 'import json; print(json.dumps({"program": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", "to": "qir"}))' > "$tmp/convert.json" +post /v1/convert "$tmp/convert.json" > "$tmp/convert.out" +py 'import json,sys; d=json.load(open(sys.argv[1])); assert d["ok"] and d["to"]=="qir" and d["written"]["profile"]=="base" and d["check"]["verdict"]["verdict"]=="equivalent", d; print("convert: ok, to QIR,", d["check"]["verdict"]["how"])' "$tmp/convert.out" +py 'import json,sys; d=json.load(open(sys.argv[1])); print(json.dumps({"source": {"format": "qir", "program": d["program"]}, "shots": 50}))' "$tmp/convert.out" > "$tmp/fromqir.json" +post /v1/run "$tmp/fromqir.json" > "$tmp/fromqir.out" +py 'import json,sys; d=json.load(open(sys.argv[1])); r=d["result"]; assert d["status"]=="done" and set(r["counts"]) <= {"00","11"} and d["request"]["source"]["format"]=="qir", d; print("a job from QIR: ok,", r["method"])' "$tmp/fromqir.out" py 'import json; print(json.dumps({"qasm": "qubit[1] q;", "thorough": True}))' > "$tmp/thorough.json" +pace code=$(curl -sS -o "$tmp/thorough.out" -w '%{http_code}' -X POST "$U/v1/shortcuts" -H 'content-type: application/json' --data-binary "@$tmp/thorough.json") [ "$code" = 422 ] || { echo "a thorough search got HTTP $code"; exit 1; } py 'import json,sys; d=json.load(open(sys.argv[1])); assert "wai-quantum shortcuts --thorough" in d["error"], d; print("thorough: ok, sent to the command line")' "$tmp/thorough.out" @@ -368,6 +404,7 @@ d=json.load(open(sys.argv[1])); r=d["result"] assert d["status"]=="done" and d["method"]=="exact" and r["accuracy"]["class"]=="bit-exact" and r["compiled"]["distance"] <= 1e-4, d print("compiled job: ok, bit-exact, within", r["compiled"]["distance"])' "$tmp/compiled-job.out" py 'import json; print(json.dumps({"qasm": "qubit[1] q;\nrz(0.3) q[0];\nh q[0];\nrz(0.4) q[0];\n", "epsilon": 1e-13}))' > "$tmp/fine.json" +pace code=$(curl -sS -o "$tmp/fine.out" -w '%{http_code}' -X POST "$U/v1/compile" -H 'content-type: application/json' --data-binary "@$tmp/fine.json") [ "$code" = 422 ] || { echo "a too-fine compile got HTTP $code"; exit 1; } py 'import json,sys; d=json.load(open(sys.argv[1])); assert "wai-quantum compile" in d["error"], d; print("fine compile: ok, sent to the command line")' "$tmp/fine.out" @@ -389,6 +426,7 @@ py 'import json; print(json.dumps({"family": "color", "distances": [3, 5], "p": post /v1/qec/threshold "$tmp/qec-threshold.json" > "$tmp/qec-threshold.out" py 'import json,sys; r=json.load(open(sys.argv[1]))["result"]; assert len(r["points"])==2 and r["below_threshold"][0]["distance_helps"], r; print("qec threshold: ok")' "$tmp/qec-threshold.out" py 'import json; print(json.dumps({"code": "bb:288"}))' > "$tmp/qec-far.json" +pace code=$(curl -sS -o "$tmp/qec-far.out" -w '%{http_code}' -X POST "$U/v1/qec/code" -H 'content-type: application/json' --data-binary "@$tmp/qec-far.json") [ "$code" = 422 ] || { echo "a long certification got HTTP $code"; exit 1; } py 'import json,sys; d=json.load(open(sys.argv[1])); assert "wai-quantum qec code bb:288" in d["error"], d; print("long certification: ok, sent to the command line")' "$tmp/qec-far.out" @@ -415,6 +453,7 @@ print("estimate: ok,", r["estimate"]["physical_qubits"], "physical qubits,", len post /v1/verify "$tmp/estimate.out" > "$tmp/estimate-verify.out" py 'import json,sys; v=json.load(open(sys.argv[1])); assert v["valid"] is True and v["reproduced"] is True and v["receipts"] is True, v; print("estimate verifies and re-runs: ok,", v["estimates"], "receipts")' "$tmp/estimate-verify.out" py 'import json; print(json.dumps({"counts": {"numQubits": 4, "rotationCount": 1000000, "rotationDepth": 10}, "budget": 1e-6}))' > "$tmp/fine-est.json" +pace code=$(curl -sS -o "$tmp/fine-est.out" -w '%{http_code}' -X POST "$U/v1/estimate" -H 'content-type: application/json' --data-binary "@$tmp/fine-est.json") [ "$code" = 422 ] || { echo "a too-fine estimate got HTTP $code"; exit 1; } py 'import json,sys; d=json.load(open(sys.argv[1])); assert "wai-quantum estimate" in d["error"], d; print("fine estimate: ok, sent to the command line")' "$tmp/fine-est.out" @@ -440,13 +479,13 @@ post /v1/repair "$tmp/proposal.json" | py 'import json,sys; d=json.load(sys.stdi py 'import json; print(json.dumps({"qasm": "OPENQASM 3.0;\nqubit[1] q;\nrx(0.3) q[0];\n", "method": "stabilizer"}))' > "$tmp/refused.json" post /v1/run "$tmp/refused.json" | py 'import json,sys; d=json.load(sys.stdin); assert d["error"].endswith("[J011]"), d; print("refusal: ok, ends with its code")' -# The whole script makes 29 submissions (30 on a host with a GPU), within the -# 30 a client may make in a window: a check added anywhere must take the place -# of one. What follows makes five. +# The script makes more submissions than a default window's 30; `pace` (above) +# keeps it within whatever window the service gives this client. # # A dry run: the plan, its estimates with their basis, the bounds, and whether # the service would take it; nothing runs. py 'import json; print(json.dumps({"qasm": "OPENQASM 3.0; qubit[30] q; h q[0]; cx q[0], q[29];", "shots": 100, "dry_run": True}))' > "$tmp/dry.json" +pace 2 post /v1/run "$tmp/dry.json" > "$tmp/dry.out" py 'import json,sys d=json.load(open(sys.argv[1])) @@ -505,6 +544,7 @@ print("sweep (mcp run_quantum_set): ok,", [round(i["result"]["expectations"][0][ # refusal comes with its code and the verdict of what was refused (the # program reads; the method named cannot take it). py 'import json; print(json.dumps({"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"dry_run_quantum_job","arguments":{"qasm":"OPENQASM 3.0; qubit[40] q; rx(0.3) q;","amplitudes":["0"*40],"method":"statevector"}}}))' > "$tmp/mcp-dry.json" +pace 2 mcp "@$tmp/mcp-dry.json" | py 'import json,sys; r=json.load(sys.stdin)["result"]; sc=r["structuredContent"]; assert not r["isError"] and sc["accepted"] is False and sc["refusal"]["code"]=="J011" and sc["verdict"]["level"]=="parsed" and sc["verdict"]["refusal"]["code"]=="J011", r; print("mcp dry run refused: ok,", r["content"][0]["text"][:60]+"…")' echo "smoke: every check held at $U" diff --git a/wai/wai-quantum-service/src/analysis.rs b/wai/wai-quantum-service/src/analysis.rs index 7dcf4be0d..ab7f3d061 100644 --- a/wai/wai-quantum-service/src/analysis.rs +++ b/wai/wai-quantum-service/src/analysis.rs @@ -25,7 +25,10 @@ //! ([`intent_bounds`]), so a check that would take long is refused before //! the job is queued, with the command that runs it locally; //! - a repair ([`Check::Repair`]) is bounded by the program's size: it reads -//! the program once a repair, at most `engine::repair::MAX_ROUNDS` times. +//! the program once a repair, at most `engine::repair::MAX_ROUNDS` times; +//! - a conversion between formats ([`Check::Convert`]) reads at most +//! [`CONVERT_MAX_BYTES`], and its check is held to the equivalence check's +//! bound. //! //! Every refusal ends with its code: J031 for what is larger than the //! service takes, J032 when every check is running. @@ -71,6 +74,7 @@ pub enum Check { Shortcuts, Compile, Repair, + Convert, } fn program<'a>(body: &'a Value, key: &str) -> Result<&'a str, (u16, String)> { @@ -283,9 +287,44 @@ pub fn bounds(check: Check, body: &Value) -> Result<(), (u16, String)> { } Check::Compile => compile_bounds(program(body, "qasm")?, body), Check::Repair => program(body, "qasm").map(|_| ()), + Check::Convert => convert_bounds(body), } } +/// The most bytes of a program a conversion here reads. +pub const CONVERT_MAX_BYTES: usize = 4 << 20; + +/// Whether the service converts `body`: a program within +/// [`CONVERT_MAX_BYTES`], whose check (the output read back and held to it) +/// is within the equivalence check's bound. The conversion's own refusals +/// are the engine's, in its answer. +pub fn convert_bounds(body: &Value) -> Result<(), (u16, String)> { + let local = "run `wai-quantum convert` on your machine"; + let size = body.get("program").and_then(Value::as_str).map_or(0, str::len) + body.get("program_base64").and_then(Value::as_str).map_or(0, |b| b.len() / 4 * 3); + if size > CONVERT_MAX_BYTES { + return Err((422, tag(refusal::TOO_LARGE_HERE, format!("a program of {size} bytes, more than a conversion here reads ({CONVERT_MAX_BYTES}); {local}")))); + } + if body.get("check") == Some(&Value::Bool(false)) { + return Ok(()); + } + // The check runs the equivalence ladder on the program read, twice at most. + let Ok(bytes) = (match (body.get("program").and_then(Value::as_str), body.get("program_base64").and_then(Value::as_str)) { + (Some(t), None) => Ok(t.as_bytes().to_vec()), + (None, Some(b)) => wai_quantum::engine::formats::decode_base64(b), + _ => Err(String::new()), + }) else { + return Ok(()); + }; + let from = body.get("from").and_then(Value::as_str).and_then(wai_quantum::engine::formats::Format::parse).unwrap_or_else(|| wai_quantum::engine::formats::Format::sniff(&bytes)); + if let Ok(r) = wai_quantum::engine::formats::read(from, &bytes) { + let work = 2.0 * equivalence_work(&r.qasm, &r.qasm); + if work > EQUIV_MAX_WORK { + return Err((422, tag(refusal::TOO_LARGE_HERE, format!("the conversion's check would apply about {work:.1e} gate amplitudes, more than the service takes ({EQUIV_MAX_WORK:.0e}); {local}, or ask with \"check\": false")))); + } + } + Ok(()) +} + /// Whether the service checks a job's intents beside its run: each /// reference within the equivalence check's bound, or the reason it does /// not (422, with the command that runs it locally). @@ -315,6 +354,12 @@ pub fn answer(check: Check, body: &Value) -> Value { Check::Improve => critique::variants(s("qasm")), Check::Shortcuts => shortcuts::shortcuts(s("qasm"), false), Check::Compile => compile::call(body).unwrap_or_else(|e| serde_json::json!({ "ok": false, "error": e })), + // A refusal as data, as both servers give one: its message and codes. + Check::Convert => wai_quantum::engine::formats::convert_json(body).unwrap_or_else(|e| { + let mut r = refusal::refusal_json(&e); + r["error"] = serde_json::json!(e); + r + }), Check::Repair => { let mut opts = body.clone(); if let Some(o) = opts.as_object_mut() { diff --git a/wai/wai-quantum-service/src/discovery.rs b/wai/wai-quantum-service/src/discovery.rs index 9e1b5868e..a89433126 100644 --- a/wai/wai-quantum-service/src/discovery.rs +++ b/wai/wai-quantum-service/src/discovery.rs @@ -286,6 +286,11 @@ pub fn openapi(server: &str) -> Value { "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["a", "b"], "properties": { "a": { "type": "string" }, "b": { "type": "string" } } } } } }, "responses": { "200": { "description": "{\"verdict\": \"equivalent\" | \"different\" | \"unchecked\", \"how\", \"why\"}" }, "422": { "description": "A check longer than the service takes; the reason names the command that runs it locally" }, "429": { "description": "Past the per-client limits" }, "503": { "description": "Every check is running" } } } }, + "/v1/convert": { "post": { + "summary": "A program carried between formats (OpenQASM 3, QIR as LLVM text, jeff as base64), the output read back and held to it by the equivalence ladder", + "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["to"], "properties": { "program": { "type": "string", "description": "The program's text: OpenQASM or QIR." }, "program_base64": { "type": "string", "description": "The program's bytes as base64: jeff." }, "from": { "enum": ["qasm", "qir", "jeff"] }, "to": { "enum": ["qasm", "qir", "jeff"] }, "profile": { "enum": ["base", "adaptive"] }, "check": { "type": "boolean" } } } } } }, + "responses": { "200": { "description": "{\"ok\": true, \"format\", \"from\", \"to\", \"program\" | \"program_base64\", \"qasm\", \"notes\", \"read\", \"written\", \"check\"}, or the refusal {\"error\", \"code\", \"codes\", \"outcome\", \"meaning\"} with the reader's code (Q700 to Q721) and place" }, "422": { "description": "A program or a check larger than the service takes" }, "429": { "description": "Past the per-client limits" }, "503": { "description": "Every check is running" } } + } }, "/v1/improve": { "post": { "summary": "The program with its smells fixed, each candidate checked the same circuit", "requestBody": { "required": true, "content": { "application/json": { "schema": prog.clone() } } }, @@ -448,7 +453,7 @@ mod tests { let d = openapi("https://example.org/q"); - for p in ["/v1", "/v1/methods", "/v1/examples", "/v1/targets", "/v1/run", "/v1/jobs", "/v1/jobs/{id}", "/v1/jobs/{id}/events", "/.well-known/wai-quantum-receipt-keys", "/v1/verify", "/v1/compile", "/v1/estimate", "/v1/qec/code", "/v1/qec/decode", "/v1/qec/threshold", "/v1/pulse/schedule", "/v1/pulse/check", "/mcp"] { + for p in ["/v1", "/v1/methods", "/v1/examples", "/v1/targets", "/v1/run", "/v1/jobs", "/v1/jobs/{id}", "/v1/jobs/{id}/events", "/.well-known/wai-quantum-receipt-keys", "/v1/verify", "/v1/compile", "/v1/estimate", "/v1/convert", "/v1/qec/code", "/v1/qec/decode", "/v1/qec/threshold", "/v1/pulse/schedule", "/v1/pulse/check", "/mcp"] { assert!(d["paths"].get(p).is_some(), "{p}"); } assert_eq!(d["openapi"], "3.1.0"); diff --git a/wai/wai-quantum-service/src/http.rs b/wai/wai-quantum-service/src/http.rs index 9e2a743b7..8f9bbd4fa 100644 --- a/wai/wai-quantum-service/src/http.rs +++ b/wai/wai-quantum-service/src/http.rs @@ -29,6 +29,8 @@ //! POST /v1/repair the program repaired on the lines its diagnostics //! flag alone, a proposed repair held to them, and //! what needs a person +//! POST /v1/convert the program carried between OpenQASM, QIR and +//! jeff, the output read back and held to it //! POST /v1/qec/code an error-correcting code, its distance certified //! POST /v1/qec/decode a syndrome, or a circuit's detectors, decoded //! POST /v1/qec/threshold logical failure rates over distances and rates, @@ -208,7 +210,7 @@ async fn describe(State(app): State) -> Response { "receipt_key": app.signer.public_key(), "receipt_keys": format!("{}/.well-known/wai-quantum-receipt-keys", app.base), "api_keys": app.keys.is_some(), - "endpoints": ["GET /v1", "GET /v1/health", "GET /v1/methods", "GET /v1/examples", "GET /v1/targets", "GET /v1/openapi.json", "GET /.well-known/wai-quantum-receipt-keys", "POST /v1/jobs", "GET /v1/jobs/{id}", "GET /v1/jobs/{id}/events", "DELETE /v1/jobs/{id}", "POST /v1/run", "POST /v1/verify", "POST /v1/critique", "POST /v1/equivalent", "POST /v1/improve", "POST /v1/shortcuts", "POST /v1/compile", "POST /v1/estimate", "POST /v1/repair", "POST /v1/qec/code", "POST /v1/qec/decode", "POST /v1/qec/threshold", "POST /mcp"], + "endpoints": ["GET /v1", "GET /v1/health", "GET /v1/methods", "GET /v1/examples", "GET /v1/targets", "GET /v1/openapi.json", "GET /.well-known/wai-quantum-receipt-keys", "POST /v1/jobs", "GET /v1/jobs/{id}", "GET /v1/jobs/{id}/events", "DELETE /v1/jobs/{id}", "POST /v1/run", "POST /v1/verify", "POST /v1/critique", "POST /v1/equivalent", "POST /v1/improve", "POST /v1/shortcuts", "POST /v1/compile", "POST /v1/estimate", "POST /v1/repair", "POST /v1/convert", "POST /v1/qec/code", "POST /v1/qec/decode", "POST /v1/qec/threshold", "POST /mcp"], "mcp": format!("{}/mcp", app.base), "console": app.console, }), @@ -270,10 +272,21 @@ pub(crate) fn plan_job(app: &App, request: &Value) -> Result<(catalog::Plan, Wor /// Plans and admits `request` for `who`: the job's id, or the refusal's /// status and reason. pub(crate) fn admit(app: &App, who: &str, request: Value) -> Result { + let request = with_source(request)?; let (plan, work) = plan_job(app, &request)?; app.service.submit_work(who, request, plan, work).map_err(|Refused(code, why)| (code, why)) } +/// A job that carries its program in another format (`"source"`), with its +/// `"qasm"` read from it (`engine::formats::expand`); any other as it is. +pub(crate) fn with_source(request: Value) -> Result { + match wai_quantum::engine::formats::expand(&request) { + Ok(Some(r)) => Ok(r), + Ok(None) => Ok(request), + Err(e) => Err((400, e)), + } +} + /// Whether a request asks only for its plan. pub(crate) fn is_dry_run(request: &Value) -> bool { request.get("dry_run") == Some(&Value::Bool(true)) @@ -285,6 +298,8 @@ pub(crate) fn is_dry_run(request: &Value) -> bool { /// the refusal it would give. It counts as one submission, as a check does. pub(crate) fn dry_run(app: &App, who: &str, request: &Value) -> Result { app.service.count(who).map_err(|Refused(code, why)| (code, why))?; + let expanded = with_source(request.clone())?; + let request = &expanded; let window = app.service.window(who); let (active, held) = app.service.holding(who); let mut out = json!({ @@ -774,6 +789,7 @@ pub fn router(app: Shared) -> Router { .route("/v1/pulse/check", post(|State(a): State, ConnectInfo(p): ConnectInfo, h: HeaderMap, b| pulse_route(a, p, h, b, "check"))) .route("/v1/estimate", post(estimate_route)) .route("/v1/repair", post(|State(a): State, ConnectInfo(p): ConnectInfo, h: HeaderMap, b| analysis(a, p, h, b, crate::analysis::Check::Repair))) + .route("/v1/convert", post(|State(a): State, ConnectInfo(p): ConnectInfo, h: HeaderMap, b| analysis(a, p, h, b, crate::analysis::Check::Convert))) .route("/v1/examples", get(examples)) .route("/v1/targets", get(targets)) .route("/v1/openapi.json", get(openapi)) diff --git a/wai/wai-quantum-service/src/mcp.rs b/wai/wai-quantum-service/src/mcp.rs index 7da681260..163814b63 100644 --- a/wai/wai-quantum-service/src/mcp.rs +++ b/wai/wai-quantum-service/src/mcp.rs @@ -63,6 +63,7 @@ fn output_of(tool: &str) -> Option<&'static str> { "verify_quantum_receipt" => "service-tools.v1.json#/$defs/verify", "critique_qasm" => "service-tools.v1.json#/$defs/critique", "check_equivalent" => "service-tools.v1.json#/$defs/equivalence", + "convert_program" => "service-tools.v1.json#/$defs/convert", "improve_qasm" => "service-tools.v1.json#/$defs/improve", "classical_shortcuts" => "service-tools.v1.json#/$defs/shortcuts", "quantum_service_info" => "service-tools.v1.json#/$defs/info", @@ -229,23 +230,26 @@ fn tool_list() -> Value { "intent": intent_schema(), "repair": { "description": "Repair the program first, on the lines its diagnostics flag alone (as repair_qasm does): true, or the choices {\"max_iterations\": N, \"dyadic_bits\": K, \"sizes\": {\"q\": 3}}. The result's `repaired` holds the program that ran and each repair." }, "mitigate": wai_quantum::engine::mitigate::schema(), - "pulse": wai_quantum::engine::pulse::options_schema() + "pulse": wai_quantum::engine::pulse::options_schema(), + "source": wai_quantum::engine::formats::source_schema() }); - let mut on_target = json!({ "type": "object", "required": ["qasm", "target"], "properties": job_props.clone() }); + let mut on_target = json!({ "type": "object", "required": ["target"], "anyOf": wai_quantum::engine::formats::program_required(), "properties": job_props.clone() }); on_target["properties"].as_object_mut().map(|o| o.remove("method")); // A set: the job's fields, shared by every item, and its programs or its sweep. let mut set_props = job_props.clone(); + set_props.as_object_mut().map(|o| o.remove("source")); set_props["qasm"] = json!({ "type": "string", "description": "With sweep: the program, which declares the inputs the sweep gives values (`input uint n;`, `input float theta;`, `input angle phi;`)." }); set_props["programs"] = json!({ "type": "array", "minItems": 1, "maxItems": wai_quantum::engine::sets::MAX_ITEMS, "items": { "anyOf": [{ "type": "string" }, { "type": "object", "required": ["qasm"] }] }, "description": "The set's programs: each a program's text, or a job ({\"qasm\", …}) whose own fields win over the set's." }); set_props["sweep"] = json!({ "type": "object", "additionalProperties": { "type": "array", "minItems": 1, "items": { "type": "number" } }, "description": "Each input of qasm and its values: every combination runs, the names in sorted order, the first varying slowest; an integer input's values are whole numbers." }); let mut plan_props = set_props.clone(); plan_props["qasm"] = job_props["qasm"].clone(); + plan_props["source"] = job_props["source"].clone(); let mut list = json!([ { "name": "run_quantum_circuit", "title": "Run a quantum circuit", "description": "Run an OpenQASM program and return measurement counts, Pauli expectation values and/or amplitudes. The service chooses the method (bit-exact or double-precision state vector up to 32 qubits, single precision on a GPU to 33, stabilizer tableau for Clifford circuits up to 32768 qubits, matrix product states up to 4096, sparse Pauli dynamics for expectation values up to 1024 with an error bound, an exact density matrix or trajectories for noise), reports why and how accurate, and signs the result. Give `intent` (an exact distribution, or a reference program it must equal or whose state it must prepare) and the engine checks it with the run. Every answer carries `verdict`: the level the engine checked to (proof, evidence, executed, parsed, refused), whether the intent holds and a plain statement, sealed in the receipt. Waits up to 50 s; a longer job answers with status running and an id for get_quantum_job.", - "inputSchema": { "type": "object", "required": ["qasm"], "properties": job_props }, + "inputSchema": { "type": "object", "anyOf": wai_quantum::engine::formats::program_required(), "properties": job_props }, "annotations": { "readOnlyHint": false, "idempotentHint": true, "openWorldHint": false } }, { @@ -377,6 +381,12 @@ fn tool_list() -> Value { } ]); if let Some(a) = list.as_array_mut() { + // The conversion between formats, the command line's server's tool, after the equivalence check. + let mut t = wai_quantum::engine::formats::tool(); + t["annotations"] = json!({ "readOnlyHint": true }); + t["description"] = json!(format!("{} A conversion larger than the service takes is refused with the command that runs it locally.", t["description"].as_str().unwrap_or(""))); + let at = a.iter().position(|t| t["name"] == "check_equivalent").map_or(a.len(), |i| i + 1); + a.insert(at, t); a.extend(wai_quantum::engine::qec::tools("the service's key").as_array().cloned().unwrap_or_default()); a.extend(wai_quantum::engine::pulse::tools("the service's key").as_array().cloned().unwrap_or_default()); } @@ -567,7 +577,10 @@ async fn call(app: &App, who: &str, name: &str, args: Value, base: &str) -> Valu "run_quantum_set" if !wai_quantum::engine::catalog::is_set(&args) => tool_result("refused: a set gives \"programs\" (a list of programs or jobs) or \"qasm\" with a \"sweep\" [J007]".into(), json!({ "error": "a set gives \"programs\" or a \"sweep\" [J007]" }), true), "run_quantum_circuit" | "run_on_target" | "run_quantum_set" => { // Every run an agent asks for states its verdict. - let mut args = args; + let mut args = match crate::http::with_source(args) { + Ok(a) => a, + Err((_, why)) => return tool_result(format!("refused: {why}"), json!({ "error": why }), true), + }; if !verdict::asked(&args) { args["verdict"] = json!(true); } @@ -738,18 +751,19 @@ async fn call(app: &App, who: &str, name: &str, args: Value, base: &str) -> Valu } Err((_, why)) => tool_result(format!("refused: {why}"), json!({ "error": why }), true), }, - "critique_qasm" | "check_equivalent" | "improve_qasm" | "classical_shortcuts" => { + "critique_qasm" | "check_equivalent" | "improve_qasm" | "classical_shortcuts" | "convert_program" => { use crate::analysis::Check; let check = match name { "critique_qasm" => Check::Critique, "check_equivalent" => Check::Equivalent, "improve_qasm" => Check::Improve, + "convert_program" => Check::Convert, _ => Check::Shortcuts, }; match crate::analysis::analyse(app, who, check, args).await { Ok(v) => { let text = v["summary"].as_str().map(str::to_string).unwrap_or_else(|| v.to_string()); - let failed = v["ok"] == false; + let failed = v["ok"] == false || (v["ok"].is_null() && v["error"].is_string()); tool_result(text, v, failed) } Err((_, why)) => tool_result(format!("refused: {why}"), json!({ "error": why }), true), @@ -807,7 +821,7 @@ mod tests { #[test] fn the_tools_are_these() { let names: Vec = tools().as_array().unwrap().iter().map(|t| t["name"].as_str().unwrap().to_string()).collect(); - assert_eq!(names, ["run_quantum_circuit", "run_quantum_set", "dry_run_quantum_job", "get_quantum_job", "verify_quantum_receipt", "critique_qasm", "check_equivalent", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "repair_qasm", "quantum_service_info", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "quantum_circuit_examples", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]); + assert_eq!(names, ["run_quantum_circuit", "run_quantum_set", "dry_run_quantum_job", "get_quantum_job", "verify_quantum_receipt", "critique_qasm", "check_equivalent", "convert_program", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "repair_qasm", "quantum_service_info", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "quantum_circuit_examples", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]); let run = &tools()[0]["inputSchema"]["properties"]["compile"]["properties"]; assert!(run.get("epsilon").is_some() && run.get("basis").is_some() && run.get("target").is_none(), "the compile's gate set is its basis; target is a device: {run}"); } @@ -822,7 +836,7 @@ mod tests { } let all = tools(); let names: Vec<&str> = all.as_array().unwrap().iter().map(|t| t["name"].as_str().unwrap()).collect(); - assert_eq!(names, ["run_quantum_circuit", "run_quantum_set", "dry_run_quantum_job", "get_quantum_job", "verify_quantum_receipt", "critique_qasm", "check_equivalent", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "repair_qasm", "quantum_service_info", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "quantum_circuit_examples", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]); + assert_eq!(names, ["run_quantum_circuit", "run_quantum_set", "dry_run_quantum_job", "get_quantum_job", "verify_quantum_receipt", "critique_qasm", "check_equivalent", "convert_program", "improve_qasm", "classical_shortcuts", "compile_qasm", "estimate_resources", "repair_qasm", "quantum_service_info", "qasm_diagnostics", "list_targets", "show_target", "run_on_target", "quantum_circuit_examples", "qec_code", "qec_decode", "qec_threshold", "pulse_schedule", "pulse_check"]); // Each declares its output, versioned; a tool that reads a program requires its verdict. for t in all.as_array().unwrap() { let (name, o) = (t["name"].as_str().unwrap(), &t["outputSchema"]); @@ -951,6 +965,11 @@ mod tests { ("critique_qasm", json!({ "qasm": bell }), false), ("critique_qasm", json!({}), true), ("check_equivalent", json!({ "a": bell, "b": "OPENQASM 3.0;\nqubit[2] q;\ncx q[0], q[1];\n" }), false), + ("convert_program", json!({ "program": bell, "to": "qir" }), false), + ("run_quantum_circuit", json!({ "source": { "format": "qir", "program": wai_quantum::engine::formats::convert_json(&json!({ "program": bell, "to": "qir" })).unwrap()["program"] }, "shots": 10 }), false), + ("run_quantum_circuit", json!({ "source": { "format": "qir", "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n" } }), true), + ("convert_program", json!({ "program": bell, "to": "jeff", "check": false }), false), + ("convert_program", json!({ "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n", "from": "qir", "to": "qasm" }), true), ("improve_qasm", json!({ "qasm": bell }), false), ("classical_shortcuts", json!({ "qasm": bell }), false), ("classical_shortcuts", json!({ "qasm": bell, "thorough": true }), true), diff --git a/wai/wai-quantum/Cargo.toml b/wai/wai-quantum/Cargo.toml index af2da66c2..4c767886e 100644 --- a/wai/wai-quantum/Cargo.toml +++ b/wai/wai-quantum/Cargo.toml @@ -12,9 +12,10 @@ keywords = ["quantum", "simulation", "wasm", "deterministic", "qec"] categories = ["science", "simulation", "wasm", "mathematics"] readme = "README.md" # Hold files outside this package (quantum-conformance/fabric/expected.json, -# quantum-conformance/qasm3/, quantum-conformance/qec-text/ and the answer -# layer's conformance set) to the crate; they run in the repository only. -exclude = ["tests/fabric_expected.rs", "tests/qasm3_conformance.rs", "tests/qec_text_conformance.rs", "tests/answer_conformance.rs"] +# quantum-conformance/qasm3/, quantum-conformance/qec-text/, the answer +# layer's conformance set and quantum-conformance/formats/) to the crate; +# they run in the repository only. +exclude = ["tests/fabric_expected.rs", "tests/qasm3_conformance.rs", "tests/qec_text_conformance.rs", "tests/answer_conformance.rs", "tests/formats_conformance.rs", "tests/formats_mutation.rs"] # docs.rs builds with DEFAULT features only, which would document 3 of 21 # modules and hide the entire operational + ML layers. Document everything. diff --git a/wai/wai-quantum/README.md b/wai/wai-quantum/README.md index bbd60a79a..f242cbd10 100644 --- a/wai/wai-quantum/README.md +++ b/wai/wai-quantum/README.md @@ -69,7 +69,7 @@ never creates information. | `quantum_toolchain` | algorithm library, backend recommendation, **OpenQASM 2/3 import through the one reader** (`from_qasm`): everything the reader takes, under the bit-exact circuit's restriction, dyadic angles and measurements at the end, refused with the reader's codes otherwise (non-dyadic angles are refused, never rounded); a controlled gate the modifiers write is held whole, so `to_qasm`'s output reads back gate for gate; **device-ready OpenQASM 2/3** (`device::device_qasm`): every gate lowered exactly to its dialect's standard library, multi-controlled gates included, every qubit measured into `c[i]`, and the service's counts read back onto the crate's basis; **native programs** (`native::compile`): a processor's own basis (`rz`/`sx`/`x`/`cz`; `prx`/`cz`; or `gpi`/`gpi2`/`zz` with angles in turns, written exactly; the Z rotations carried in the phases of the last two) on its own qubits, routed over its coupling map, and checked against the circuit by simulation before it is sent — Bloch / entanglement / purity analysis | | `quantum_lang` | **the OpenQASM reader, one grammar for every surface** (`qasm::read`, `qasm::parse`): the job service, the command line, both MCP servers, the Studio, the desktop app, the edge component and the in-browser IDE all read through it. OpenQASM 3.1 and the OpenQASM 2 forms in common use, read in three stages with the source position of everything: registers, aliases and slices; gate definitions; every modifier (`ctrl @`, `negctrl @`, `inv @`, `pow(k) @`, exact fractional powers) on any gate, lowered exactly; `def` subroutines, inlined, a returned measurement written into the caller's bit; classical types (`int`, `uint`, `float`, `angle[n]`, `complex`, `bool`, `bit`, arrays), their arithmetic, casts and built-in functions, computed on reading with the same bits on every platform; `for`, `while` (unrolled when known, or run at most `@wai.max_iterations N` times on measured bits), `switch`, `break`, `continue`, `end`; conditions on measured bits with comparisons, `&&`, `||` and `!`, compiled to steps that branch forward; `output` carried into the result; `delay`, `box`, `stretch` and `barrier` carried as timing and labelled when a method ignores durations; `defcal` and `cal` kept verbatim, never run. Generators: integer `input`s, so one program describes a family of circuits (`qasm::read_with`, `qasm::instantiate`). **Every refusal and warning carries a stable code** (`line 3: unknown gate foo [Q103]`, and as data with its line, column and length): [the table of codes](docs/qasm-diagnostics.md) (`qasm::diag::codes()`, `wai-quantum diagnostics`), and [what the reader does with each construct of OpenQASM 3.1](docs/qasm-feature-matrix.md). The [conformance corpus](../quantum-conformance/qasm3/) holds the specification's examples and a program for each construct, run natively, under wasm32-wasip2 and in the browser | | `quantum_qasm` | the reader's program lowered to each method's input, exactly up to a global phase: dense matrices, two-qubit matrices for the matrix product state, Pauli rotations, Clifford gates, and the dyadic circuit (`Program::dyadic`); `qasm::dyadic_circuit` gives the exact state vector's circuit for a program whose measurements all come at the end, and refuses any other with its code | -| `quantum_engine` | **the quantum job service's engine, for every host**: a job (an OpenQASM program and what is asked of it: shots, Pauli expectation values, amplitudes, noise) planned onto the strongest method it admits, with the reason and the memory and work it will take; run; answered in the service's JSON with its accuracy class; and sealed over the request and the result in their canonical JSON (`engine::job`, `engine::receipt`), so a result from the service, the command line or a browser tab checks the same way. Also a program's circuit drawn as text (`engine::draw`) and as a standalone SVG drawing, the Studio's picture with each gate marked by whose hand it is in (`engine::svg`, which the Python package draws in notebooks: `wai/wai-quantum-py`); the specification-and-evaluator workbench (`engine::discover`: a specification, the engine's verdict on a generator at every size, and an archive of the cheapest verified generator in every cell of two measures, sealed); a program's history, attributed (`engine::versions`: versions in a sealed chain, each with who made it and how, proposals reviewed change by change, and each line and gate in the hand that wrote it, which a job's receipt can bind); the person's rules for proposals (`engine::locks`: held blocks, the gates allowed, the qubit declarations, what to avoid and a region, checked change by change); what is new in a program (`engine::novelty`: its structural distance to the known constructions and to programs given, each known construction it is proved to be and whether by a different route, and the strengths a cost did not ask for; a workbench candidate that breaks only a rule is an aberration, with the cost it would have); a study (`engine::study`: a protocol sealed before any data, each participant's condition fixed by their key, their record sealed with it, the measures computed by the engine from what they made, and a cohort report with seeded bootstrap intervals and Cronbach's alpha); convergence (`engine::converge`: along a program's history, how far the person's versions are built from a model's proposals and the model's share of the gates, with the versions made on their own set beside the rest; and a cohort's diversity, its Vendi score, centroid and narrowing over time); rewrites that keep a circuit's meaning (`engine::rewrite`: cancellations, merges, Hadamard conjugations and gates carried past what they commute with, each checked on the qubits it touches, so a chain of them proves two programs equal at any width, explains an optimisation, and offers a person one rewrite at a time); a person's taste (`engine::taste`: a Bradley–Terry model of their own choices between programs the engine proves equal, part by part of a rubric, with typicality shown beside it and moved only by a dial, and how well it predicts them measured); what an agent is told (`engine::guide`: the language the reader takes, its diagnostics, each held to the reader by a test, the methods and their accuracy classes, worked examples each with its intent, and the rules an agent keeps, which both MCP servers serve as resources and prompts); the language service an editor needs (`engine::editor`: the reader's refusal and the critique's smells as diagnostics on their lines, completion by what the cursor's place takes, hover with each standard gate's matrix written exactly and held to the engine's own, a user gate's computed from its gates, and each gate's cost as the engine counts it, definitions, references, renames, an outline, folding, and a formatter with one layout that keeps every token, all pure and with no protocol in it); compilation with a certificate (`engine::compile`: checked passes, every rotation written in Clifford+T within an ε budget by `engine::synth`, one sealed record of each pass and the distance; see [docs/compiler.md](docs/compiler.md)); resource estimation from programs (`engine::estimate`: logical counts with each rotation at its synthesised `T` count, the physical estimate with the code model fitted to the crate's own decoders, the frontier, every estimate sealed; see [docs/estimate.md](docs/estimate.md)); the debugger (`engine::debug`: a program stepped forward and back one operation at a time on the bit-exact or double-precision state vector, or the density matrix under noise beside its noiseless state, stopped before a line, and probed anywhere for its amplitudes and phases, each qubit's Bloch vector and purity, the reduced density matrix of chosen qubits and the entanglement across a cut; every branch at its measurements in the middle with its weight, exact fractions by the tableau for a noiseless Clifford program; a recorded path replayed; a state seen twice the same bits, and the exact method's state after the last gate the exact run's, by its hash; refused past 16 qubits, or 8 under noise); the circuit as LaTeX in plain TikZ (`engine::latex`) and in words for a screen reader (`engine::describe`); the circuit edited by its drawing, a gate put on a wire, moved or deleted as an edit to the program's own text through its syntax tree (`engine::visual`); Pearson's chi-squared test and the G-test of sampled outcomes against a stated distribution, the p-value the same bits everywhere (`engine::stats`); sets and sweeps (`engine::sets`: a list of programs, or one program over a grid of its integer, float and angle inputs, run as plain jobs, each item sealed and one receipt over the head that holds the Merkle root of every item's leaf, with each item's path to it); a result's provenance under the categories provenance surveys use (`engine::run::run_with_provenance`: the program's digest, the method, each pass a compile ran with its check's verdict and the certificate's digest, the shots, seed and plan, the engine's version); and the energy a job spent (`engine::energy`: the CPU energy the kernel counts for one process on macOS, and on Linux the processor packages' counters over a window, read only when live and labelled with no attribution to the job). It builds for `wasm32-unknown-unknown`, where it runs on one thread and claims no time | +| `quantum_engine` | **the quantum job service's engine, for every host**: a job (an OpenQASM program and what is asked of it: shots, Pauli expectation values, amplitudes, noise) planned onto the strongest method it admits, with the reason and the memory and work it will take; run; answered in the service's JSON with its accuracy class; and sealed over the request and the result in their canonical JSON (`engine::job`, `engine::receipt`), so a result from the service, the command line or a browser tab checks the same way. Also a program's circuit drawn as text (`engine::draw`) and as a standalone SVG drawing, the Studio's picture with each gate marked by whose hand it is in (`engine::svg`, which the Python package draws in notebooks: `wai/wai-quantum-py`); the specification-and-evaluator workbench (`engine::discover`: a specification, the engine's verdict on a generator at every size, and an archive of the cheapest verified generator in every cell of two measures, sealed); a program's history, attributed (`engine::versions`: versions in a sealed chain, each with who made it and how, proposals reviewed change by change, and each line and gate in the hand that wrote it, which a job's receipt can bind); the person's rules for proposals (`engine::locks`: held blocks, the gates allowed, the qubit declarations, what to avoid and a region, checked change by change); what is new in a program (`engine::novelty`: its structural distance to the known constructions and to programs given, each known construction it is proved to be and whether by a different route, and the strengths a cost did not ask for; a workbench candidate that breaks only a rule is an aberration, with the cost it would have); a study (`engine::study`: a protocol sealed before any data, each participant's condition fixed by their key, their record sealed with it, the measures computed by the engine from what they made, and a cohort report with seeded bootstrap intervals and Cronbach's alpha); convergence (`engine::converge`: along a program's history, how far the person's versions are built from a model's proposals and the model's share of the gates, with the versions made on their own set beside the rest; and a cohort's diversity, its Vendi score, centroid and narrowing over time); rewrites that keep a circuit's meaning (`engine::rewrite`: cancellations, merges, Hadamard conjugations and gates carried past what they commute with, each checked on the qubits it touches, so a chain of them proves two programs equal at any width, explains an optimisation, and offers a person one rewrite at a time); a person's taste (`engine::taste`: a Bradley–Terry model of their own choices between programs the engine proves equal, part by part of a rubric, with typicality shown beside it and moved only by a dial, and how well it predicts them measured); what an agent is told (`engine::guide`: the language the reader takes, its diagnostics, each held to the reader by a test, the methods and their accuracy classes, worked examples each with its intent, and the rules an agent keeps, which both MCP servers serve as resources and prompts); the language service an editor needs (`engine::editor`: the reader's refusal and the critique's smells as diagnostics on their lines, completion by what the cursor's place takes, hover with each standard gate's matrix written exactly and held to the engine's own, a user gate's computed from its gates, and each gate's cost as the engine counts it, definitions, references, renames, an outline, folding, and a formatter with one layout that keeps every token, all pure and with no protocol in it); compilation with a certificate (`engine::compile`: checked passes, every rotation written in Clifford+T within an ε budget by `engine::synth`, one sealed record of each pass and the distance; see [docs/compiler.md](docs/compiler.md)); resource estimation from programs (`engine::estimate`: logical counts with each rotation at its synthesised `T` count, the physical estimate with the code model fitted to the crate's own decoders, the frontier, every estimate sealed; see [docs/estimate.md](docs/estimate.md)); the debugger (`engine::debug`: a program stepped forward and back one operation at a time on the bit-exact or double-precision state vector, or the density matrix under noise beside its noiseless state, stopped before a line, and probed anywhere for its amplitudes and phases, each qubit's Bloch vector and purity, the reduced density matrix of chosen qubits and the entanglement across a cut; every branch at its measurements in the middle with its weight, exact fractions by the tableau for a noiseless Clifford program; a recorded path replayed; a state seen twice the same bits, and the exact method's state after the last gate the exact run's, by its hash; refused past 16 qubits, or 8 under noise); the circuit as LaTeX in plain TikZ (`engine::latex`) and in words for a screen reader (`engine::describe`); the circuit edited by its drawing, a gate put on a wire, moved or deleted as an edit to the program's own text through its syntax tree (`engine::visual`); Pearson's chi-squared test and the G-test of sampled outcomes against a stated distribution, the p-value the same bits everywhere (`engine::stats`); sets and sweeps (`engine::sets`: a list of programs, or one program over a grid of its integer, float and angle inputs, run as plain jobs, each item sealed and one receipt over the head that holds the Merkle root of every item's leaf, with each item's path to it); a result's provenance under the categories provenance surveys use (`engine::run::run_with_provenance`: the program's digest, the method, each pass a compile ran with its check's verdict and the certificate's digest, the shots, seed and plan, the engine's version); and the energy a job spent (`engine::energy`: the CPU energy the kernel counts for one process on macOS, and on Linux the processor packages' counters over a window, read only when live and labelled with no attribution to the job).; and programs in other formats (`engine::formats`): QIR read under the base and adaptive profiles its entry point names (1.x and 2.x, what a profile forbids refused on its line, a rotation at any angle read at its exact double) and written as QIR 2.0, and jeff read and written through the crate's own Cap'n Proto reader and writer (loops over registers unrolled, a switch on a measured bit read as a condition, Pauli-product rotations lowered exactly), every construct a reader cannot hold refused with a code (`Q700` to `Q721`) and its place and each conversion checked by the equivalence ladder ([the formats page](docs/formats.md), [the conformance set](../quantum-conformance/formats/)). It builds for `wasm32-unknown-unknown`, where it runs on one thread and claims no time | | `quantum_stabilizer` | stabilizer (CHP) tableau — `O(n²)`, scales far past statevector | | `quantum_mps` | tensor-network (matrix-product-state) backend | | `quantum_pauli` | sparse-Pauli / Heisenberg observable propagation over the dyadic gate set (up to 64 qubits) | @@ -100,7 +100,7 @@ never creates information. | `quantum_sv` | **the dense state vector at any angle**: one-, two- and `k`-qubit kernels with a diagonal fast path, gate fusion around multi-qubit gates, threads over independent blocks; every kernel path computes each amplitude in the same order, so threading never changes a bit. Agrees with an independent simulator to 2·10⁻¹⁶ | | `quantum_compile` | Clifford routing + stabilizer-tableau equivalence proof | | `quantum_atom` | neutral-atom register preparation (Hungarian / LSAP) | -| `quantum_qir` | **QIR export + ingest** — emit and parse QIR (the QIR Alliance's LLVM-based IR); emitted modules validated with `llvm-as`, and 9 of 10 catalog algorithms re-ingest to a bit-identical statevector | +| `quantum_qir` | **QIR export + ingest** for the bit-exact circuit — emit and parse QIR (the QIR Alliance's LLVM-based IR) in Clifford+T and dyadic phases; emitted modules validated with `llvm-as`, and 9 of 10 catalog algorithms re-ingest to a bit-identical statevector. The engine's own QIR reader and writer (`engine::formats::qir`, with `quantum_engine`) take any angle and both profiles | | `quantum_comm` | **key distribution and security**: BB84 with an intercept-resend eavesdropper (a quiet channel gives a perfect key; interception costs a quarter of it), and the no-cloning theorem priced | | `quantum_qudit` | **qubits and qutrits**: the Weyl–Heisenberg group, the Fourier gate over `Z_d`, the controlled sum — exact for `d = 2` and `3` | | `quantum_info` | **mixed states**: density matrices, the partial trace, purity, exact Pauli expectations, and CHSH against the classical and Tsirelson bounds | diff --git a/wai/wai-quantum/README.zh-CN.md b/wai/wai-quantum/README.zh-CN.md index 14dd7e3be..d3cb0c017 100644 --- a/wai/wai-quantum/README.zh-CN.md +++ b/wai/wai-quantum/README.zh-CN.md @@ -52,7 +52,7 @@ wai-quantum lab qutrit # d = 3,不只是量子比特 | `quantum_toolchain` | 算法库、后端推荐、**通过唯一的读取器导入 OpenQASM 2/3**(`from_qasm`):读取器接受的一切内容,只要符合逐位精确线路的限制——二进角度、测量位于末尾——都可导入,否则以读取器的代码拒绝(非二进角度会被拒绝,绝不舍入);由修饰符写出的受控门保持为一个整体,因此 `to_qasm` 的输出能逐门读回;**可直接用于设备的 OpenQASM 2/3**(`device::device_qasm`):每个门都被精确降级(lower)到其方言的标准库,包括多控制门,每个量子比特都测量到 `c[i]` 中,服务返回的计数再读回到本 crate 的基上;**原生程序**(`native::compile`):处理器自己的基(`rz`/`sx`/`x`/`cz`;`prx`/`cz`;或角度以圈为单位、精确写出的 `gpi`/`gpi2`/`zz`;Z 旋转承载在后两者的相位中),作用于处理器自身的量子比特,在其耦合图上路由,并在发送之前通过模拟与原线路核对——Bloch / 纠缠 / 纯度分析 | | `quantum_lang` | **OpenQASM 读取器,所有入口共用一套语法**(`qasm::read`、`qasm::parse`):作业服务、命令行、两个 MCP 服务器、Studio、桌面应用、边缘组件和浏览器内 IDE 都通过它读取。支持 OpenQASM 3.1 以及常用的 OpenQASM 2 写法,分三个阶段读取,并保留每个元素的源代码位置:寄存器、别名与切片;门定义;任意门上的每一种修饰符(`ctrl @`、`negctrl @`、`inv @`、`pow(k) @`、精确的分数次幂),均精确降级;`def` 子程序,内联展开,返回的测量结果写入调用方的比特;经典类型(`int`、`uint`、`float`、`angle[n]`、`complex`、`bool`、`bit`、数组)及其算术、类型转换和内置函数,在读取时求值,在所有平台上得到相同的位;`for`、`while`(次数已知时展开,否则基于测量比特最多运行 `@wai.max_iterations N` 次)、`switch`、`break`、`continue`、`end`;基于测量比特、带比较运算以及 `&&`、`||` 和 `!` 的条件,编译为向前分支的步骤;`output` 带入结果;`delay`、`box`、`stretch` 和 `barrier` 作为时序信息保留,并在某种方法忽略时长时加以标注;`defcal` 和 `cal` 原样保留,从不执行。生成器:整数 `input`,使一个程序可以描述一族线路(`qasm::read_with`、`qasm::instantiate`)。**每一条拒绝和警告都带有稳定的代码**(`line 3: unknown gate foo [Q103]`,也以数据形式给出,附带行、列和长度):[代码表](docs/qasm-diagnostics.md)(`qasm::diag::codes()`、`wai-quantum diagnostics`),以及[读取器如何处理 OpenQASM 3.1 的每种构造](docs/qasm-feature-matrix.md)。[一致性语料库](../quantum-conformance/qasm3/)收录了规范中的示例以及针对每种构造的程序,分别在原生环境、wasm32-wasip2 下和浏览器中运行 | | `quantum_qasm` | 把读取器得到的程序降级为各方法的输入,在相差一个全局相位的意义下精确:稠密矩阵、供矩阵乘积态使用的两量子比特矩阵、Pauli 旋转、Clifford 门,以及二进线路(`Program::dyadic`);对于所有测量都位于末尾的程序,`qasm::dyadic_circuit` 给出精确态矢量所用的线路,其他程序则以相应代码拒绝 | -| `quantum_engine` | **量子作业服务的引擎,适用于所有宿主**:一个作业(一个 OpenQASM 程序以及对它的要求:采样次数(shots)、Pauli 期望值、振幅、噪声)被规划到它所允许的最强方法上,并给出理由以及将占用的内存和工作量;然后运行;以服务的 JSON 返回结果并附上精度等级;再以请求和结果的规范 JSON 加以封存(`engine::job`、`engine::receipt`),因此无论结果来自服务、命令行还是浏览器标签页,都以同样的方式校验。此外还有:以文本绘制程序的线路(`engine::draw`),以及绘制为独立的 SVG 图——即 Studio 中的那幅图,每个门都标明出自谁之手(`engine::svg`,Python 包在 notebook 中用它绘图:`wai/wai-quantum-py`);规范与评估器工作台(`engine::discover`:一份规范,引擎对某个生成器在每个规模上的判定,以及一个存档,记录由两项度量划分出的每个格子中已验证的最廉价生成器,并加以封存);带署名的程序历史(`engine::versions`:版本构成一条封存的链,每个版本记录由谁、以何种方式产生,提案逐项变更地审阅,每一行、每个门都归于写下它的人,作业的回执可以绑定这些信息);使用者为提案设定的规则(`engine::locks`:锁定的代码块、允许的门、量子比特声明、需要避免的内容以及一个区域,逐项变更地检查);程序中有什么新东西(`engine::novelty`:它与已知构造及给定程序之间的结构距离,它被证明等同于哪些已知构造、是否经由不同的路径,以及代价函数并未要求的长处;只违反某条规则的工作台候选被视为偏离(aberration),并附上它本应具有的代价);研究(`engine::study`:在获得任何数据之前就封存的研究方案,每位参与者的实验条件由其密钥固定,其记录也用该密钥封存,各项度量由引擎根据参与者的产出计算,以及带有固定种子的 bootstrap 区间和 Cronbach's alpha 的群组报告);趋同(`engine::converge`:沿着程序的历史,使用者的版本在多大程度上建立在模型的提案之上、模型在门中所占的份额,并把使用者独立完成的版本与其余版本并列;以及一个群组的多样性、其 Vendi 分数、质心和随时间的收窄);保持线路含义的重写(`engine::rewrite`:消去、合并、Hadamard 共轭,以及把门移过与之对易的门,每一步都在它触及的量子比特上加以检查,因此一连串重写可以在任意宽度上证明两个程序相等、解释一次优化,并且每次向使用者提供一个重写);使用者的偏好(`engine::taste`:针对使用者在引擎证明等价的程序之间所作的选择,按评分细则逐项建立的 Bradley–Terry 模型,典型性显示在旁边且只能通过一个调节旋钮改变,并测量它对使用者选择的预测效果);告诉智能体的内容(`engine::guide`:读取器接受的语言、它的诊断信息——每一项都由测试保证与读取器一致——各种方法及其精度等级、每个都附有意图的示例,以及智能体须遵守的规则,两个 MCP 服务器都将其作为资源和提示词提供);编辑器所需的语言服务(`engine::editor`:读取器的拒绝和评审(critique)发现的代码异味作为诊断显示在相应的行上,根据光标所在位置可接受的内容进行补全,悬停提示中每个标准门的矩阵精确写出并与引擎自身的矩阵核对、用户定义门的矩阵由其组成门计算得出,以及引擎所计的每个门的代价,定义、引用、重命名、大纲、折叠,以及只有一种排版、保留每个词法单元的格式化器,全部是纯逻辑,不含任何协议);带证书的编译(`engine::compile`:经过检查的各遍处理(pass),由 `engine::synth` 在 ε 预算内把每个旋转写成 Clifford+T,每遍处理及距离都记录在一份封存的记录中;参见 [docs/compiler.md](docs/compiler.md));基于程序的资源估算(`engine::estimate`:逻辑计数,其中每个旋转按综合后的 `T` 门数计;物理估算,其码模型拟合自本 crate 自己的解码器;前沿(frontier);每个估算都加以封存;参见 [docs/estimate.md](docs/estimate.md));调试器(`engine::debug`:在逐位精确或双精度的态矢量上,或在有噪声的密度矩阵上(与其无噪声的态并列),逐个操作地向前和向后单步执行程序,在某一行之前停下,并在任意位置探查其振幅和相位、每个量子比特的 Bloch 矢量和纯度、所选量子比特的约化密度矩阵以及跨某个切分的纠缠;在中途测量处给出每个分支及其权重,对于无噪声的 Clifford 程序则由稳定子表给出精确分数;重放已录制的路径;同一个态看两次,位完全相同,而精确方法在最后一个门之后的态就是精确运行所得的态,以其哈希值为证;超过 16 个量子比特(有噪声时超过 8 个)则拒绝);以纯 TikZ 写成 LaTeX 的线路(`engine::latex`),以及供屏幕阅读器使用的文字描述(`engine::describe`);通过线路图编辑线路:在一根线上放置、移动或删除一个门,都会经由语法树转化为对程序文本本身的编辑(`engine::visual`);针对给定分布、对采样结果进行的 Pearson 卡方检验和 G 检验,p 值在任何地方都是相同的位(`engine::stats`);以及操作系统内核为单个进程统计的 CPU 能耗(`engine::energy`)。它可以为 `wasm32-unknown-unknown` 构建,在该目标上单线程运行,且不声称任何耗时 | +| `quantum_engine` | **量子作业服务的引擎,适用于所有宿主**:一个作业(一个 OpenQASM 程序以及对它的要求:采样次数(shots)、Pauli 期望值、振幅、噪声)被规划到它所允许的最强方法上,并给出理由以及将占用的内存和工作量;然后运行;以服务的 JSON 返回结果并附上精度等级;再以请求和结果的规范 JSON 加以封存(`engine::job`、`engine::receipt`),因此无论结果来自服务、命令行还是浏览器标签页,都以同样的方式校验。此外还有:以文本绘制程序的线路(`engine::draw`),以及绘制为独立的 SVG 图——即 Studio 中的那幅图,每个门都标明出自谁之手(`engine::svg`,Python 包在 notebook 中用它绘图:`wai/wai-quantum-py`);规范与评估器工作台(`engine::discover`:一份规范,引擎对某个生成器在每个规模上的判定,以及一个存档,记录由两项度量划分出的每个格子中已验证的最廉价生成器,并加以封存);带署名的程序历史(`engine::versions`:版本构成一条封存的链,每个版本记录由谁、以何种方式产生,提案逐项变更地审阅,每一行、每个门都归于写下它的人,作业的回执可以绑定这些信息);使用者为提案设定的规则(`engine::locks`:锁定的代码块、允许的门、量子比特声明、需要避免的内容以及一个区域,逐项变更地检查);程序中有什么新东西(`engine::novelty`:它与已知构造及给定程序之间的结构距离,它被证明等同于哪些已知构造、是否经由不同的路径,以及代价函数并未要求的长处;只违反某条规则的工作台候选被视为偏离(aberration),并附上它本应具有的代价);研究(`engine::study`:在获得任何数据之前就封存的研究方案,每位参与者的实验条件由其密钥固定,其记录也用该密钥封存,各项度量由引擎根据参与者的产出计算,以及带有固定种子的 bootstrap 区间和 Cronbach's alpha 的群组报告);趋同(`engine::converge`:沿着程序的历史,使用者的版本在多大程度上建立在模型的提案之上、模型在门中所占的份额,并把使用者独立完成的版本与其余版本并列;以及一个群组的多样性、其 Vendi 分数、质心和随时间的收窄);保持线路含义的重写(`engine::rewrite`:消去、合并、Hadamard 共轭,以及把门移过与之对易的门,每一步都在它触及的量子比特上加以检查,因此一连串重写可以在任意宽度上证明两个程序相等、解释一次优化,并且每次向使用者提供一个重写);使用者的偏好(`engine::taste`:针对使用者在引擎证明等价的程序之间所作的选择,按评分细则逐项建立的 Bradley–Terry 模型,典型性显示在旁边且只能通过一个调节旋钮改变,并测量它对使用者选择的预测效果);告诉智能体的内容(`engine::guide`:读取器接受的语言、它的诊断信息——每一项都由测试保证与读取器一致——各种方法及其精度等级、每个都附有意图的示例,以及智能体须遵守的规则,两个 MCP 服务器都将其作为资源和提示词提供);编辑器所需的语言服务(`engine::editor`:读取器的拒绝和评审(critique)发现的代码异味作为诊断显示在相应的行上,根据光标所在位置可接受的内容进行补全,悬停提示中每个标准门的矩阵精确写出并与引擎自身的矩阵核对、用户定义门的矩阵由其组成门计算得出,以及引擎所计的每个门的代价,定义、引用、重命名、大纲、折叠,以及只有一种排版、保留每个词法单元的格式化器,全部是纯逻辑,不含任何协议);带证书的编译(`engine::compile`:经过检查的各遍处理(pass),由 `engine::synth` 在 ε 预算内把每个旋转写成 Clifford+T,每遍处理及距离都记录在一份封存的记录中;参见 [docs/compiler.md](docs/compiler.md));基于程序的资源估算(`engine::estimate`:逻辑计数,其中每个旋转按综合后的 `T` 门数计;物理估算,其码模型拟合自本 crate 自己的解码器;前沿(frontier);每个估算都加以封存;参见 [docs/estimate.md](docs/estimate.md));调试器(`engine::debug`:在逐位精确或双精度的态矢量上,或在有噪声的密度矩阵上(与其无噪声的态并列),逐个操作地向前和向后单步执行程序,在某一行之前停下,并在任意位置探查其振幅和相位、每个量子比特的 Bloch 矢量和纯度、所选量子比特的约化密度矩阵以及跨某个切分的纠缠;在中途测量处给出每个分支及其权重,对于无噪声的 Clifford 程序则由稳定子表给出精确分数;重放已录制的路径;同一个态看两次,位完全相同,而精确方法在最后一个门之后的态就是精确运行所得的态,以其哈希值为证;超过 16 个量子比特(有噪声时超过 8 个)则拒绝);以纯 TikZ 写成 LaTeX 的线路(`engine::latex`),以及供屏幕阅读器使用的文字描述(`engine::describe`);通过线路图编辑线路:在一根线上放置、移动或删除一个门,都会经由语法树转化为对程序文本本身的编辑(`engine::visual`);针对给定分布、对采样结果进行的 Pearson 卡方检验和 G 检验,p 值在任何地方都是相同的位(`engine::stats`);以及操作系统内核为单个进程统计的 CPU 能耗(`engine::energy`)。它可以为 `wasm32-unknown-unknown` 构建,在该目标上单线程运行,且不声称任何耗时;以及其他格式的程序(`engine::formats`):按入口点所声明的基础(base)或自适应(adaptive)配置文件读取 QIR(1.x 和 2.x;配置文件禁止的内容按行拒绝,任意角度的旋转按其精确的双精度值读取),并写出为 QIR 2.0;通过本 crate 自带的 Cap'n Proto 读写器读写 jeff。读取器无法精确表示的每种构造都以代码(`Q700` 到 `Q721`)及其位置拒绝,每次转换都由等价判定阶梯检查([格式说明](docs/formats.md)、[一致性测试集](../quantum-conformance/formats/)) | | `quantum_stabilizer` | 稳定子(CHP)表——`O(n²)`,可扩展的规模远超态矢量 | | `quantum_mps` | 张量网络(矩阵乘积态)后端 | | `quantum_pauli` | 在二进门集上进行稀疏 Pauli / Heisenberg 绘景的可观测量传播(最多 64 个量子比特) | diff --git a/wai/wai-quantum/docs/README.md b/wai/wai-quantum/docs/README.md index b4f2d5732..afe489a58 100644 --- a/wai/wai-quantum/docs/README.md +++ b/wai/wai-quantum/docs/README.md @@ -33,6 +33,8 @@ run one says why. - [The reader's diagnostics](qasm-diagnostics.md) and [what it does with each construct](qasm-feature-matrix.md). - [The job layer's refusal codes](job-refusals.md). +- [Other formats](formats.md): QIR under its profiles and jeff, read and + written, each conversion checked. - [Error correction and mitigation](qec.md): codes with their certified distances, the decoders, thresholds, and mitigation in a job. - [Pulse and dynamics](pulse.md): pulse programs read into schedules on diff --git a/wai/wai-quantum/docs/README.zh-CN.md b/wai/wai-quantum/docs/README.zh-CN.md index 3e01f27af..3e060889d 100644 --- a/wai/wai-quantum/docs/README.zh-CN.md +++ b/wai/wai-quantum/docs/README.zh-CN.md @@ -20,6 +20,7 @@ - [编译器](compiler.md):各遍处理、ε 预算和证书。 - [资源估算](estimate.md):估算中的每个数字及其假设。 - [读取器的诊断](qasm-diagnostics.md),以及[它如何处理每种构造](qasm-feature-matrix.md)。 +- [其他格式](formats.md):按配置文件读写的 QIR 和 jeff,每次转换都经过检查。 - [作业层的拒绝代码](job-refusals.md)。 - [纠错与误差缓解](qec.md):码及其经过认证的距离、解码器、阈值,以及作业中的误差缓解。 - [脉冲与动力学](pulse.md):读入为帧上时序的脉冲程序,在主方程下积分并给出误差上界,在每个平台上比特一致,以及其回执所绑定的内容。 diff --git a/wai/wai-quantum/docs/formats.md b/wai/wai-quantum/docs/formats.md new file mode 100644 index 000000000..4aba9ccc4 --- /dev/null +++ b/wai/wai-quantum/docs/formats.md @@ -0,0 +1,196 @@ +# Other formats: QIR and jeff + +The engine reads programs in two formats beside OpenQASM and writes them +(`wai_quantum::engine::formats`). A reader gives the program as OpenQASM 3, +which the one reader every surface uses then reads, so a program from any +format plans, runs, checks and draws as one written in OpenQASM. A +construct a reader cannot hold exactly is refused with a code (`Q700` to +`Q721`, in [the table of codes](qasm-diagnostics.md)) and where it stands: a +line of a QIR module, or a function and an operation of a jeff module. + +| Format | Read | Written | +|---|---|---| +| QIR, LLVM text | 1.x (typed pointers) and 2.x (opaque pointers), under the base and adaptive profiles | 2.0, opaque pointers: the base profile, or the adaptive profile for a program that measures in the middle, resets or branches | +| jeff, its Cap'n Proto message | 0.2 and 0.3 | 0.3.1 | + +## Converting + +```sh runnable +wai-quantum convert prog.qasm --to qir > prog.ll # QIR 2.0, base profile +wai-quantum convert teleport.qasm --to qir > teleport.ll # it branches: the adaptive profile +wai-quantum convert prog.ll --to jeff -o prog.jeff # the format read is found from the file +wai-quantum convert prog.jeff --to qasm # as OpenQASM 3 +wai-quantum convert prog.jeff --to qasm --json > back.json # with the notes, what was read and the check +wai-quantum run prog.ll --shots 1000 # every verb that reads a program reads these +cp prog.jeff prog.bin +wai-quantum lint --from jeff prog.bin # a name the extension does not give +``` + +Every conversion is checked. What is written is read back and held to the +program read by the equivalence ladder (a tableau, every basis state at up +to 8 qubits, rewrites, random product states, or unchecked with the +reason). A program read from QIR or jeff is written back to its format and +read again, and the two readings are held to each other the same way. A +program that measures in the middle or branches is held step for step, and +where its steps differ (a condition on several bits written as one branch +per bit, say) by the probability of every outcome, branch by branch with +its weight, which is evidence and says so. + +The same conversion is the MCP tool `convert_program` on both servers, the +job service's `POST /v1/convert`, and `engine_convert` in the browser +engine. A job may carry its program in another format: +`{"source": {"format": "qir", "program": "…"}}` in place of `"qasm"` +(`"program_base64"` for jeff); it is read before anything else reads the +job, and the request keeps its source. + +## QIR + +**Profiles.** The entry point's `qir_profiles` names the profile, and the +module is held to it: + +- **base**: gates, then measurements, then output records. A branch, a + classical instruction, reading a result, or a gate or reset on a qubit + after its measurement is what the profile forbids (`Q704`). +- **adaptive**: measurements anywhere; results read as booleans + (`__quantum__rt__read_result`, or QIR 1's `__quantum__rt__result_equal` + with `result_get_one`) and branched on, forward; `i1` logic (`and`, `or`, + `xor`, `icmp eq` and `ne`, `select`). An optional capability its module + flags do not declare is forbidden (`Q704`). Integer and float computation + the flags declare is computed on reading where its operands are known + (an angle computed from constants); computation on a measurement anywhere + but a condition, loops, `switch`, `phi`, IR-defined functions, dynamic + qubits or results and arrays are not taken here, though declared + (`Q705`). + +A module that names no profile is read under the profile its instructions +need, and one that leaves out the version flags or the qubit and result +counts is read from what it holds; each says so (`Q702`). A profile other +than these two is refused (`Q703`), as is a major version other than 1 or 2 +(`Q701`). + +**The program.** Qubit `i` is `q[i]` and result `r` is `c[r]`; the control +flow, a tree of forward branches, is written as nested `if` and `else`, +each branch running to the block where its paths meet again. A result read, +measured again and only then branched on is refused (`Q708`): the text would +read the later measurement. Output records are listed in what was read +(`recorded`); classical values recorded as output are not carried (`Q721`). + +**Intrinsics read.** `__quantum__qis____body`, and `__adj` for the +inverse: + +| Intrinsic | OpenQASM | Intrinsic | OpenQASM | +|---|---|---|---| +| `x` `y` `z` `h` | `x` `y` `z` `h` | `cnot`, `cx` | `cx` | +| `s`, `s__adj` | `s`, `sdg` | `cy` `cz` `swap` | `cy` `cz` `swap` | +| `t`, `t__adj` | `t`, `tdg` | `ccx` | `ccx` | +| `sx` | `sx` | `ccz` | `ctrl(2) @ z` | +| `rx` `ry` `rz` (angle first) | `rx` `ry` `rz` | `rxx` `ryy` `rzz` | their basis changes, two `cx` and one `rz` | +| `r1` | `p` | `mz`, `m`, `mresetz`, `reset`, `barrier` | `measure`, `reset`; a barrier is left out | + +A module of up to 16 MiB is read; its branches nest at most 64 deep, and +are written out to at most 1 048 576 statements, 4 194 304 instructions run +over every path and a bounded copy of each path's state (`Q705`). A +condition holds at most 1 024 terms, nested at most 32 deep once written. + +Any other intrinsic is refused (`Q706`), and so is a `__ctl` form, whose +controls come in an array (`Q705`). Angles are read at their exact doubles, +LLVM's hex form (`double 0x400921FB54442D18`) or decimal. A Clifford+T +module stays Clifford+T and runs on the bit-exact method; a rotation at an +angle that is not a dyadic multiple of 2π reads with a note (`Q709`): the +double-precision methods take it, the bit-exact method refuses it. + +**Writing.** QIR 2.0 with opaque pointers, the four mandatory module flags +(and, under the adaptive profile, `ir_functions`, `backwards_branching`, +`multiple_target_branching` and `multiple_return_points`, each off), one +`array_record_output` and a `result_record_output` for every bit. The base +profile's four blocks; under the adaptive profile, one block per step a +branch reaches, a condition on several bits read bit by bit with +`__quantum__rt__read_result` and `br` alone. Gates are written as the +intrinsics above where one exists; the others are lowered exactly, up to a +global phase (`U` as `rz ry rz`, `cp` and the controlled rotations by their +standard identities, `ch`, `cu`, `iswap` and `cswap` by theirs), and a phase +at a multiple of π/4 is written as `z`, `s` and `t`, so a Clifford+T program +stays Clifford+T. Every angle is written as its exact hex double. A program +whose steps jump backwards is refused (`Q720`), as is a program that branches +when the base profile is asked for. + +## jeff + +**The module.** A list of functions, each a dataflow graph of operations +over values a qubit uses exactly once. The reader runs the entry point's +graph in an order its values allow: + +- **Qubits**: each `alloc` is a new qubit, in the order the reader reaches + it; a register (`qureg`) of a size known on reading is its qubits, + extracted, inserted, sliced, split and joined by index. An entry point + that takes qubits takes them as the program's first qubits, from `|0⟩`, + and returns them unmeasured (`Q716`). +- **Gates**: the well-known gates (`x y z s t h`, `i` as `id`, `r1` as `p`, + `rx ry rz`, `u` as `U`, `swap`, `gphase`), with their control qubits + given after their targets, as the schema says, and `adjoint` and `power` + (0 and 1 both apply the gate once) as `ctrl @`, `inv @` and `pow @`. A + Pauli-product rotation is `exp(iθP)`, as the schema defines it, lowered + exactly to basis changes, a parity ladder and one `rz` (`Q712`). A custom + gate is read only when its name is a gate of the standard library, in any + case, with that gate's qubits and parameters, and says so (`Q711`); any + other is refused (`Q710`). +- **Measurement**: `measure` and `measureNd` each write a new bit, in the + order the reader reaches them; when the entry point returns its bits + (one-bit integers, or arrays of them), the program's bits are those, in + that order. `reset` resets. +- **Classical values** known on reading are computed then, the same bits on + every platform: integers at their width, floats through the crate's own + functions. A measured bit may be negated, combined with `and`, `or`, + `xor`, `eq` and `select`, widened, and branched on; other arithmetic on it + is refused (`Q714`). +- **Control flow** (`Q715`): a `for` whose bounds are known, and a `while` + whose condition is, unrolled; a `switch` on a known value decided; a + `switch` on a measured bit written as `if … else`, a bit set on one side + joined with the other side's as a condition; and a call of a function the + module defines inlined, up to 32 deep. + +A value used twice, before it is defined or as the wrong type is refused +(`Q713`); a value the reader needs on reading and cannot know is refused +(`Q714`), as is a qubit the two sides of a branch leave in different places +(`Q708`). Metadata is not read. The reader takes modules of up to 16 MiB, runs up to 4 194 304 operations +(`Q312`), writes up to 1 048 576 gates (`Q210`) and 65 536 qubits (`Q111`, +a register's size checked before it is made), and holds up to 65 536 +elements in one array and 4 194 304 registers' slots, array elements, +values and condition terms in all (`Q714`). + +**The wire format.** A Cap'n Proto message, read and written by a reader +and writer of its own (no dependency): segments, far pointers and every +element size, each pointer bounds-checked in 64 bits, at most 4 Mi words +visited (a list of values counting each element) and 64 pointers deep, so a +message that points many times at one object, loops, or states a size past +its end does no more work than that. + +**Writing.** One function, `main`: every qubit allocated in order, each gate +a well-known gate, exactly, global phase included (controlled gates with +their control qubits, `sx` as `rx(π/2)` and a global phase, `rxx`, `ryy` +and `rzz` as Pauli-product rotations, `iswap` by its Clifford identity, `cu` +as a controlled `u` and the phase on its control), each measurement written +into a bit array at its bit's index, which `main` returns, and every qubit +not measured last freed. A program that branches on what it measured is +refused (`Q720`); one that measures in the middle or resets is written with +`measureNd` and `reset`. + +## Hostile input + +Every reader refuses rather than allocates what a file's sizes ask for: +each size, count, index and bound is held to a cap before anything is +made from it, and every list a message holds counts against the words it +may visit. The check a conversion runs is bounded too: past 262 144 gates +applied or 4·10⁹ amplitudes touched it is not run, and the answer says +`unchecked` with the reason (`wai-quantum equivalent` runs it on its own). +`tests/formats_mutation.rs` holds every file of the conformance set, +mutated by a seeded generator, to that: each input converts or is refused +with a code, within its time and memory, natively and under wasm32-wasip2. + +## Conformance + +`wai/quantum-conformance/formats/` holds a module in each format for each +construct, with the program it must read to, the checks that hold it (the +same circuit by the ladder, the same counts, a round trip, the notes it +gives) and the refusals with their codes and places; CI runs it natively +and under wasm32-wasip2 (`wai-quantum-formats.yml`). diff --git a/wai/wai-quantum/docs/qasm-diagnostics.md b/wai/wai-quantum/docs/qasm-diagnostics.md index 063205fea..fbb98b799 100644 --- a/wai/wai-quantum/docs/qasm-diagnostics.md +++ b/wai/wai-quantum/docs/qasm-diagnostics.md @@ -140,5 +140,24 @@ engine. A test holds this page to the code's table. | `Q502` | refused | A program that measures a qubit and then acts on it, resets one after its first gate, or acts on measured bits: the bit-exact circuit takes programs whose measurements all come at the end. | | `Q600` | carried | An `output` declaration, carried into the result: its bits (their places in each outcome) or its value. | | `Q601` | refused | An `output` that is not a bit and whose value depends on a measurement at the end. | +| `Q700` | refused | A file that does not read in the format named: QIR that is not LLVM text with one entry point, or jeff that is not a Cap'n Proto message of the jeff schema (a pointer outside the message, a struct or list of the wrong shape, more than the reader takes); the message says what and where. | +| `Q701` | refused | A format version the reader does not take: QIR whose `qir_major_version` is not 1 or 2, or jeff other than 0.2 and 0.3. | +| `Q702` | warned | A QIR module that leaves out what its profile requires (the `qir_profiles` attribute, the version flags, `required_num_qubits`, `required_num_results`, the `entry_point` attribute of its only function): read from what it holds, and said so. | +| `Q703` | refused | A QIR profile the reader does not take: `qir_profiles` names neither `base_profile` nor `adaptive_profile`. | +| `Q704` | refused | What the module's own QIR profile forbids: under the base profile a branch, a classical instruction, reading a result, or a gate or reset on a qubit after its measurement; under the adaptive profile an optional capability its module flags do not declare (computation on integers or floats of a type, IR-defined functions, loops, `switch`, several returns, dynamic qubits or results, arrays). | +| `Q705` | refused | A QIR capability the module may use that this reader does not take: loops, `switch`, `phi`, IR-defined functions, dynamic allocation of qubits or results, arrays, controlled forms that take their controls in an array, or a classical value computed from a measurement anywhere but a branch's condition. | +| `Q706` | refused | An instruction or function the reader does not know: a quantum intrinsic outside the gates it reads (`docs/formats.md` lists them), a runtime function it does not take, an LLVM instruction no profile lists, or a function a module declares and does not define. | +| `Q707` | refused | A QIR operand that does not read: a qubit or result that is not a constant index, an index past `required_num_qubits` or `required_num_results`, or an angle that is not a finite number known on reading. | +| `Q708` | refused | A value whose meaning the program's text cannot keep: in QIR a result read, measured again and only then branched on; in jeff a qubit the two sides of a branch on a measurement leave in different places. | +| `Q709` | warned | Rotations at angles that are not dyadic multiples of 2π, read at their exact doubles: the double-precision methods take them; the bit-exact method refuses them (Q500). | +| `Q710` | refused | A jeff custom gate the reader does not know: its name is not a gate of the standard library (in any case), or its qubits or parameters are not that gate's. | +| `Q711` | warned | A jeff custom gate named as a gate of the standard library (`H`, `CX`, `Rx`, in any case) with that gate's qubits and parameters: read as that gate, by its name alone. | +| `Q712` | lowered | A jeff Pauli-product rotation, `exp(iθP)` as its schema defines it, lowered exactly to basis changes, a parity ladder and one `rz`. | +| `Q713` | refused | A jeff value used where it cannot be: a qubit or register used twice, a value used before it is defined or never defined, a cycle, or a value of the wrong type or count for its operation. | +| `Q714` | refused | A jeff value the reader needs on reading and cannot know: a gate angle, a loop's bounds or a `while` condition from a measurement, a register's size or index, a `switch` on a value that is neither known nor a measured bit, or integer arithmetic on a measured bit beyond not, and, or, xor, eq and select. | +| `Q715` | lowered | jeff's structured control flow: a `for` or `while` decided on reading unrolled, a `switch` on a known value decided, a `switch` on a measured bit written as `if … else`, and a call of a function the module defines inlined. | +| `Q716` | warned | A jeff entry point that takes qubits: they are the program's first qubits, starting in \|0⟩, and the qubits it returns are not measured. | +| `Q720` | refused | A program the format written cannot hold as it is: under QIR's base profile a measurement in the middle, a reset after a gate or a condition (write the adaptive profile), a loop in QIR, or in jeff a branch on a measured bit. | +| `Q721` | warned | What the format read or written has no place for, left out and said so: delays, boxes, barriers and gate durations, calibrations, `output` declarations, and classical values recorded as output. | -See `qasm-feature-matrix.md` for each construct of OpenQASM 3.1 and its code. +See `qasm-feature-matrix.md` for each construct of OpenQASM 3.1 and its code. The codes from `Q700` are those of the readers and writers of other formats (QIR and jeff, `formats.md`). diff --git a/wai/wai-quantum/schemas/job-request.v1.json b/wai/wai-quantum/schemas/job-request.v1.json index 8fc6d70c0..ee8ae8154 100644 --- a/wai/wai-quantum/schemas/job-request.v1.json +++ b/wai/wai-quantum/schemas/job-request.v1.json @@ -14,6 +14,11 @@ "required": [ "programs" ] + }, + { + "required": [ + "source" + ] } ], "properties": { @@ -21,6 +26,30 @@ "type": "string", "description": "The program, in OpenQASM 3 or the OpenQASM 2 forms in common use." }, + "source": { + "type": "object", + "required": [ + "format" + ], + "description": "The program in another format, in place of qasm (engine::formats::expand): read to the job's qasm before the job is read, the request keeping its source.", + "properties": { + "format": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program's text (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program's bytes as base64 (jeff)." + } + } + }, "shots": { "type": "integer", "minimum": 0, diff --git a/wai/wai-quantum/schemas/tools.v1.json b/wai/wai-quantum/schemas/tools.v1.json index b3d5c3ff5..77542fdcd 100644 --- a/wai/wai-quantum/schemas/tools.v1.json +++ b/wai/wai-quantum/schemas/tools.v1.json @@ -2095,6 +2095,138 @@ "type": "integer" } } + }, + "convert": { + "type": "object", + "description": "A program carried from one format to another (OpenQASM 3, QIR as LLVM text, jeff as its Cap'n Proto message) and checked (engine::formats::convert_json): the program written, the program as read in OpenQASM 3, the readers' and writer's notes with their codes, what was read and written, and the check that holds the output to the input.", + "required": [ + "ok", + "format", + "from", + "to", + "qasm", + "notes", + "read", + "written", + "check" + ], + "properties": { + "ok": { + "const": true + }, + "format": { + "const": "wai.quantum.convert/1" + }, + "from": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "to": { + "enum": [ + "qasm", + "qir", + "jeff" + ] + }, + "program": { + "type": "string", + "description": "The program written, for a text format (OpenQASM, QIR)." + }, + "program_base64": { + "type": "string", + "description": "The program written, for a binary format (jeff), as standard base64." + }, + "qasm": { + "type": "string", + "description": "The program as read, as OpenQASM 3: what every other tool takes." + }, + "notes": { + "type": "array", + "items": { + "$ref": "#/$defs/diagnostic" + }, + "description": "Each warning and note, with its code (Q7xx for the formats) and, for QIR, its line." + }, + "read": { + "type": "object", + "required": [ + "format" + ], + "description": "What was read: the format, its version, a QIR module's profile, entry point, qubits, results and declared capabilities, a jeff module's tool, entry point, qubits and bits." + }, + "written": { + "type": "object", + "required": [ + "format" + ], + "description": "What was written: the format, its version, and QIR's profile." + }, + "check": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "required": [ + "what", + "verdict" + ], + "properties": { + "what": { + "type": "string", + "description": "What was held to what." + }, + "verdict": { + "type": "object", + "required": [ + "verdict", + "how", + "why" + ], + "properties": { + "verdict": { + "enum": [ + "equivalent", + "different", + "unchecked" + ] + }, + "how": { + "enum": [ + "tableau", + "exact", + "rewrites", + "random", + "state", + "shape", + "steps", + "distribution", + "identity", + "none" + ], + "description": "The equivalence ladder's check (tableau, exact and rewrites are proofs; random is evidence); steps: the same steps one for one; distribution: every outcome's probability the same (evidence); identity: OpenQASM passed through; none: unchecked." + }, + "why": { + "type": "string" + }, + "global_phase": { + "type": [ + "number", + "null" + ] + } + } + } + } + } + ], + "description": "The output read back and held to the program read (or, for a program read into OpenQASM, written back to its format and read again), or null when no check was asked." + } + } } } } diff --git a/wai/wai-quantum/src/engine/formats/capnp.rs b/wai/wai-quantum/src/engine/formats/capnp.rs new file mode 100644 index 000000000..8e9eed68d --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/capnp.rs @@ -0,0 +1,703 @@ +//! The Cap'n Proto wire format, as far as a schema-driven exchange format +//! needs it: a message read from its segments (structs, lists of every +//! element size, text and far pointers) with every pointer bounds-checked and +//! a limit on the words a reader may visit, and a message written as one +//! segment in preorder, each object after the one that points to it. +//! +//! The layout of each struct (which word holds a field, which pointer slot a +//! list sits in) is the schema's, and is the caller's business: this module +//! knows the encoding, not any schema. +//! +//! - A message is a segment table (the segment count less one, then each +//! segment's size in words, as little-endian `u32`s, padded to a word) +//! followed by the segments. +//! - A pointer is one word. Its low two bits are its kind: `0` a struct (a +//! signed offset in words from the end of the pointer, the data section's +//! size in words, the pointer section's), `1` a list (offset, element size, +//! count), `2` a far pointer to a landing pad in another segment, `3` a +//! capability, which a message on disk never holds. +//! - A list's element size is `0` void, `1` a bit, `2` a byte, `3` two bytes, +//! `4` four, `5` eight, `6` a pointer, `7` a struct each (composite), with a +//! tag word before the elements that gives their count and size. + +/// The most words a reader visits in one message: a list or struct read +/// twice counts twice, so a message that points many times at one object +/// cannot make the reader do more work than this. A list of values counts +/// one for each element (a list of bits or bytes as well), so what a reader +/// makes of a list is bounded by it too. +pub const TRAVERSAL_LIMIT_WORDS: u64 = 4 << 20; +/// The most segments a message may have. +pub const MAX_SEGMENTS: usize = 512; +/// The deepest the reader follows pointers. +pub const MAX_DEPTH: u32 = 64; + +/// A message read: its segments, and what is left of its traversal budget. +pub struct Message<'a> { + segments: Vec<&'a [u8]>, + budget: std::cell::Cell, +} + +/// A struct inside a message. +#[derive(Clone, Copy)] +pub struct Struct<'a> { + msg: &'a Message<'a>, + seg: usize, + /// Byte offset of the data section in its segment. + data: usize, + data_words: u16, + /// Byte offset of the pointer section in its segment. + ptrs: usize, + ptr_count: u16, + depth: u32, +} + +/// A list inside a message. +#[derive(Clone, Copy)] +pub struct List<'a> { + msg: &'a Message<'a>, + seg: usize, + /// Byte offset of the first element. + start: usize, + /// The element size code (0 to 7). + size: u8, + len: u32, + /// For a composite list: each element's data and pointer words. + data_words: u16, + ptr_count: u16, + depth: u32, +} + +fn word(seg: &[u8], byte: usize) -> u64 { + u64::from_le_bytes(seg[byte..byte + 8].try_into().expect("eight bytes")) +} + +/// Whether `bytes` from byte `start` lie within a segment of `len` bytes, +/// in 64 bits, so no size a message gives can overflow a 32-bit address. +fn within(start: usize, bytes: u64, len: usize) -> bool { + (start as u64).checked_add(bytes).is_some_and(|e| e <= len as u64) +} + +impl<'a> Message<'a> { + /// Reads a message's segment table. + pub fn read(bytes: &'a [u8]) -> Result, String> { + if bytes.len() < 8 { + return Err(format!("{} bytes: shorter than a segment table", bytes.len())); + } + let count = u64::from(u32::from_le_bytes(bytes[0..4].try_into().expect("four bytes"))) + 1; + if count > MAX_SEGMENTS as u64 { + return Err(format!("{count} segments: more than the {MAX_SEGMENTS} a message may have here")); + } + let count = count as usize; + let table = (4 + 4 * count).div_ceil(8) * 8; + if bytes.len() < table { + return Err("the segment table runs past the end of the file".into()); + } + let mut segments = Vec::with_capacity(count); + let mut at = table; + for i in 0..count { + let words = u64::from(u32::from_le_bytes(bytes[4 + 4 * i..8 + 4 * i].try_into().expect("four bytes"))); + if !within(at, words * 8, bytes.len()) { + return Err(format!("segment {i} ({words} words) runs past the end of the file")); + } + let end = at + (words * 8) as usize; + segments.push(&bytes[at..end]); + at = end; + } + if at != bytes.len() { + return Err(format!("{} bytes after the last segment", bytes.len() - at)); + } + if segments[0].len() < 8 { + return Err("the first segment holds no root pointer".into()); + } + Ok(Message { segments, budget: std::cell::Cell::new(TRAVERSAL_LIMIT_WORDS) }) + } + + fn spend(&self, words: u64) -> Result<(), String> { + let left = self.budget.get(); + if words > left { + return Err(format!("the message points at more than the {TRAVERSAL_LIMIT_WORDS} words a reader visits here")); + } + self.budget.set(left - words); + Ok(()) + } + + /// The root struct. + pub fn root(&'a self) -> Result, String> { + self.struct_at(0, 0, 0)?.ok_or_else(|| "the root pointer is null".to_string()) + } + + /// The pointer at byte `at` of segment `seg`, followed through a far + /// pointer: the segment and byte offset of the object, and the pointer + /// word that describes it (a struct's or list's own). + fn resolve(&'a self, seg: usize, at: usize) -> Result, String> { + let s = self.segments[seg]; + if !within(at, 8, s.len()) { + return Err(format!("a pointer at word {} of segment {seg} is past its end", at / 8)); + } + let p = word(s, at); + if p == 0 { + return Ok(None); + } + match p & 3 { + 0 | 1 => { + let off = ((p as u32 as i32) >> 2) as i64; + let target = (at as i64 + 8) + off * 8; + if target < 0 || target as u64 > s.len() as u64 { + return Err(format!("a pointer at word {} of segment {seg} points outside it", at / 8)); + } + Ok(Some((seg, target as usize, p))) + } + 2 => { + let double = p >> 2 & 1 == 1; + let pad = u64::from((p as u32) >> 3) * 8; + let tseg = (p >> 32) as usize; + let ts = *self.segments.get(tseg).ok_or_else(|| format!("a far pointer names segment {tseg}, which the message does not have"))?; + if !within(0, pad + if double { 16 } else { 8 }, ts.len()) { + return Err("a far pointer's landing pad is past its segment's end".into()); + } + let pad = pad as usize; + if !double { + let inner = word(ts, pad); + if inner & 3 == 2 { + return Err("a far pointer lands on another far pointer".into()); + } + return self.resolve(tseg, pad); + } + let far = word(ts, pad); + let tag = word(ts, pad + 8); + if far & 7 != 2 { + return Err("a double-far landing pad does not start with a far pointer".into()); + } + let cseg = (far >> 32) as usize; + let cs = *self.segments.get(cseg).ok_or_else(|| format!("a far pointer names segment {cseg}, which the message does not have"))?; + let content = u64::from((far as u32) >> 3) * 8; + if !within(0, content, cs.len()) || tag & 3 > 1 || (tag as u32) >> 2 != 0 { + return Err("a double-far pointer's tag or content does not read".into()); + } + Ok(Some((cseg, content as usize, tag))) + } + _ => Err("a capability pointer: a message on disk holds none".into()), + } + } + + fn struct_at(&'a self, seg: usize, at: usize, depth: u32) -> Result>, String> { + if depth > MAX_DEPTH { + return Err(format!("pointers nested deeper than {MAX_DEPTH}")); + } + let Some((seg, start, p)) = self.resolve(seg, at)? else { return Ok(None) }; + if p & 3 != 0 { + return Err("a list where a struct is expected".into()); + } + let data_words = (p >> 32) as u16; + let ptr_count = (p >> 48) as u16; + let words = data_words as usize + ptr_count as usize; + if !within(start, words as u64 * 8, self.segments[seg].len()) { + return Err("a struct runs past the end of its segment".into()); + } + self.spend(words.max(1) as u64)?; + Ok(Some(Struct { msg: self, seg, data: start, data_words, ptrs: start + data_words as usize * 8, ptr_count, depth })) + } + + fn list_at(&'a self, seg: usize, at: usize, depth: u32) -> Result>, String> { + if depth > MAX_DEPTH { + return Err(format!("pointers nested deeper than {MAX_DEPTH}")); + } + let Some((seg, start, p)) = self.resolve(seg, at)? else { return Ok(None) }; + if p & 3 != 1 { + return Err("a struct where a list is expected".into()); + } + let size = (p >> 32 & 7) as u8; + let count = (p >> 35) as u32; + let s = self.segments[seg]; + if size == 7 { + // `count` is the words of every element; the tag before them says how many. + if !within(start, 8, s.len()) { + return Err("a list's tag is past the end of its segment".into()); + } + let tag = word(s, start); + if tag & 3 != 0 { + return Err("a composite list's tag is not a struct's".into()); + } + let len = ((tag as u32) >> 2) as usize; + let (dw, pc) = ((tag >> 32) as u16, (tag >> 48) as u16); + let per = dw as usize + pc as usize; + if len.checked_mul(per).is_none_or(|w| w > count as usize) { + return Err("a composite list's elements do not fit the words it gives".into()); + } + if !within(start, 8 + u64::from(count) * 8, s.len()) { + return Err("a list runs past the end of its segment".into()); + } + // An element of no words still costs a word to visit. + self.spend((count as u64).max(len as u64))?; + return Ok(Some(List { msg: self, seg, start: start + 8, size, len: len as u32, data_words: dw, ptr_count: pc, depth })); + } + let bits: u64 = match size { + 0 => 0, + 1 => 1, + 2 => 8, + 3 => 16, + 4 => 32, + 5 | 6 => 64, + _ => unreachable!(), + }; + let bytes = (u64::from(count) * bits).div_ceil(8); + if !within(start, bytes, s.len()) { + return Err("a list runs past the end of its segment".into()); + } + self.spend(bytes.div_ceil(8).max(u64::from(count)).max(1))?; + Ok(Some(List { msg: self, seg, start, size, len: count, data_words: 0, ptr_count: 0, depth })) + } +} + +impl<'a> Struct<'a> { + fn seg(&self) -> &'a [u8] { + self.msg.segments[self.seg] + } + + fn bytes(&self, byte: usize) -> Option<[u8; N]> { + (byte + N <= self.data_words as usize * 8).then(|| self.seg()[self.data + byte..self.data + byte + N].try_into().expect("N bytes")) + } + + /// The `u8` at byte `byte` of the data section (0 past its end, as an + /// older writer's struct reads). + pub fn u8(&self, byte: usize) -> u8 { + self.bytes::<1>(byte).map_or(0, |b| b[0]) + } + /// The `u16` at element `i` (bytes `2i`, `2i + 1`). + pub fn u16(&self, i: usize) -> u16 { + self.bytes::<2>(2 * i).map_or(0, u16::from_le_bytes) + } + /// The `u32` at element `i` (bytes `4i` to `4i + 3`). + pub fn u32(&self, i: usize) -> u32 { + self.bytes::<4>(4 * i).map_or(0, u32::from_le_bytes) + } + /// The `u64` at element `i`. + pub fn u64(&self, i: usize) -> u64 { + self.bytes::<8>(8 * i).map_or(0, u64::from_le_bytes) + } + /// The `f32` at element `i`, from its bits. + pub fn f32(&self, i: usize) -> f32 { + f32::from_bits(self.u32(i)) + } + /// The `f64` at element `i`, from its bits. + pub fn f64(&self, i: usize) -> f64 { + f64::from_bits(self.u64(i)) + } + /// The bit at bit offset `bit` of the data section. + pub fn bool(&self, bit: usize) -> bool { + self.u8(bit / 8) >> (bit % 8) & 1 == 1 + } + + fn ptr_at(&self, i: usize) -> Option { + (i < self.ptr_count as usize).then(|| self.ptrs + i * 8) + } + + /// The struct pointer slot `i` holds, if it is not null. + pub fn get_struct(&self, i: usize) -> Result>, String> { + match self.ptr_at(i) { + Some(at) => self.msg.struct_at(self.seg, at, self.depth + 1), + None => Ok(None), + } + } + + /// The list pointer slot `i` holds, if it is not null. + pub fn get_list(&self, i: usize) -> Result>, String> { + match self.ptr_at(i) { + Some(at) => self.msg.list_at(self.seg, at, self.depth + 1), + None => Ok(None), + } + } + + /// The text pointer slot `i` holds: a list of bytes ending in a NUL, in + /// UTF-8. Null reads as empty. + pub fn get_text(&self, i: usize) -> Result { + match self.get_list(i)? { + None => Ok(String::new()), + Some(l) => l.text(), + } + } +} + +impl<'a> List<'a> { + pub fn len(&self) -> usize { + self.len as usize + } + + #[allow(dead_code)] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + fn seg(&self) -> &'a [u8] { + self.msg.segments[self.seg] + } + + /// Element `i` of a list of structs. + pub fn get_struct(&self, i: usize) -> Result, String> { + if i >= self.len() { + return Err(format!("element {i} of a list of {}", self.len)); + } + if self.size != 7 { + return Err("a list of values where a list of structs is expected".into()); + } + let per = (self.data_words as usize + self.ptr_count as usize) * 8; + let at = self.start + i * per; + Ok(Struct { msg: self.msg, seg: self.seg, data: at, data_words: self.data_words, ptrs: at + self.data_words as usize * 8, ptr_count: self.ptr_count, depth: self.depth }) + } + + fn need(&self, size: u8, what: &str) -> Result<(), String> { + if self.size != size { + return Err(format!("a list of element size {} where a list of {what} is expected", self.size)); + } + Ok(()) + } + + /// A list of `u32`s. + pub fn u32s(&self) -> Result, String> { + self.need(4, "32-bit values")?; + let s = self.seg(); + Ok((0..self.len()).map(|i| u32::from_le_bytes(s[self.start + 4 * i..self.start + 4 * i + 4].try_into().expect("four bytes"))).collect()) + } + /// A list of `u16`s. + pub fn u16s(&self) -> Result, String> { + self.need(3, "16-bit values")?; + let s = self.seg(); + Ok((0..self.len()).map(|i| u16::from_le_bytes(s[self.start + 2 * i..self.start + 2 * i + 2].try_into().expect("two bytes"))).collect()) + } + /// A list of bytes. + pub fn u8s(&self) -> Result, String> { + self.need(2, "bytes")?; + Ok(self.seg()[self.start..self.start + self.len()].to_vec()) + } + /// A list of `u64`s. + pub fn u64s(&self) -> Result, String> { + self.need(5, "64-bit values")?; + let s = self.seg(); + Ok((0..self.len()).map(|i| word(s, self.start + 8 * i)).collect()) + } + /// A list of bits. + pub fn bools(&self) -> Result, String> { + self.need(1, "bits")?; + let s = self.seg(); + Ok((0..self.len()).map(|i| s[self.start + i / 8] >> (i % 8) & 1 == 1).collect()) + } + /// Text: a list of bytes that ends in a NUL, in UTF-8. + pub fn text(&self) -> Result { + let b = self.u8s()?; + match b.split_last() { + Some((0, body)) => String::from_utf8(body.to_vec()).map_err(|_| "text that is not UTF-8".to_string()), + None => Ok(String::new()), + _ => Err("text that does not end in a NUL".into()), + } + } + /// A list of text. + pub fn texts(&self) -> Result, String> { + self.need(6, "pointers")?; + (0..self.len()) + .map(|i| match self.msg.list_at(self.seg, self.start + 8 * i, self.depth + 1)? { + None => Ok(String::new()), + Some(l) => l.text(), + }) + .collect() + } +} + +// --------------------------------------------------------------------------- +// Writing +// --------------------------------------------------------------------------- + +/// A struct to write: its data words and its pointer slots, as the schema +/// sizes them. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Out { + pub data: Vec, + pub ptrs: Vec, +} + +/// What a pointer slot holds. +#[derive(Clone, Debug, Default, PartialEq)] +pub enum Ptr { + #[default] + Null, + Struct(Box), + /// Structs of the same data and pointer sizes, as a composite list. + Structs { data_words: u16, ptr_count: u16, items: Vec }, + #[allow(dead_code)] + U8s(Vec), + U16s(Vec), + U32s(Vec), + #[allow(dead_code)] + U64s(Vec), + Bools(Vec), + Text(String), + Texts(Vec), +} + +impl Out { + /// A struct of `data_words` data words and `ptr_count` null pointers. + pub fn new(data_words: usize, ptr_count: usize) -> Out { + Out { data: vec![0; data_words], ptrs: vec![Ptr::Null; ptr_count] } + } + pub fn set_u8(&mut self, byte: usize, v: u8) { + let w = &mut self.data[byte / 8]; + let sh = (byte % 8) * 8; + *w = (*w & !(0xff << sh)) | (u64::from(v) << sh); + } + pub fn set_u16(&mut self, i: usize, v: u16) { + let w = &mut self.data[i / 4]; + let sh = (i % 4) * 16; + *w = (*w & !(0xffff << sh)) | (u64::from(v) << sh); + } + pub fn set_u32(&mut self, i: usize, v: u32) { + let w = &mut self.data[i / 2]; + let sh = (i % 2) * 32; + *w = (*w & !(0xffff_ffff << sh)) | (u64::from(v) << sh); + } + pub fn set_f64(&mut self, i: usize, v: f64) { + self.data[i] = v.to_bits(); + } + pub fn set_bool(&mut self, bit: usize, v: bool) { + let w = &mut self.data[bit / 64]; + let m = 1u64 << (bit % 64); + *w = if v { *w | m } else { *w & !m }; + } +} + +/// `root` as a message of one segment, every object written after the +/// pointer to it (a preorder, depth first): the same bytes for the same +/// struct, on every platform. +pub fn write(root: &Out) -> Vec { + let mut w: Vec = vec![0]; + place_struct(&mut w, 0, root); + let mut out = Vec::with_capacity(8 + w.len() * 8); + out.extend_from_slice(&0u32.to_le_bytes()); + out.extend_from_slice(&(w.len() as u32).to_le_bytes()); + for x in w { + out.extend_from_slice(&x.to_le_bytes()); + } + out +} + +fn offset(ptr: usize, target: usize) -> u64 { + (((target as i64 - ptr as i64 - 1) as i32 as u32) << 2) as u64 +} + +fn struct_ptr(ptr: usize, target: usize, data: usize, ptrs: usize) -> u64 { + offset(ptr, target) | (data as u64) << 32 | (ptrs as u64) << 48 +} + +fn list_ptr(ptr: usize, target: usize, size: u64, count: usize) -> u64 { + offset(ptr, target) | 1 | size << 32 | (count as u64) << 35 +} + +/// Writes `s` at the end of `w` and points slot `ptr` at it. +fn place_struct(w: &mut Vec, ptr: usize, s: &Out) { + let at = w.len(); + w.extend_from_slice(&s.data); + let ptrs = w.len(); + w.extend(std::iter::repeat_n(0, s.ptrs.len())); + w[ptr] = struct_ptr(ptr, at, s.data.len(), s.ptrs.len()); + for (i, p) in s.ptrs.iter().enumerate() { + place(w, ptrs + i, p); + } +} + +fn pack(items: &[T], bytes: usize, to: impl Fn(T) -> u64) -> Vec { + let per = 8 / bytes; + items.chunks(per).map(|c| c.iter().enumerate().fold(0u64, |acc, (i, &x)| acc | to(x) << (i * bytes * 8))).collect() +} + +fn place(w: &mut Vec, ptr: usize, p: &Ptr) { + let at = w.len(); + match p { + Ptr::Null => {} + Ptr::Struct(s) => place_struct(w, ptr, s), + Ptr::Structs { data_words, ptr_count, items } => { + let per = *data_words as usize + *ptr_count as usize; + w.push((items.len() as u64) << 2 | u64::from(*data_words) << 32 | u64::from(*ptr_count) << 48); + let first = w.len(); + for s in items { + debug_assert_eq!((s.data.len(), s.ptrs.len()), (*data_words as usize, *ptr_count as usize)); + w.extend_from_slice(&s.data); + w.extend(std::iter::repeat_n(0, s.ptrs.len())); + } + w[ptr] = list_ptr(ptr, at, 7, items.len() * per); + for (k, s) in items.iter().enumerate() { + let ptrs = first + k * per + *data_words as usize; + for (i, q) in s.ptrs.iter().enumerate() { + place(w, ptrs + i, q); + } + } + } + Ptr::U8s(v) => { + w.extend(pack(v, 1, u64::from)); + w[ptr] = list_ptr(ptr, at, 2, v.len()); + } + Ptr::Text(t) => { + let mut b = t.as_bytes().to_vec(); + b.push(0); + w.extend(pack(&b, 1, u64::from)); + w[ptr] = list_ptr(ptr, at, 2, b.len()); + } + Ptr::U16s(v) => { + w.extend(pack(v, 2, u64::from)); + w[ptr] = list_ptr(ptr, at, 3, v.len()); + } + Ptr::U32s(v) => { + w.extend(pack(v, 4, u64::from)); + w[ptr] = list_ptr(ptr, at, 4, v.len()); + } + Ptr::U64s(v) => { + w.extend_from_slice(v); + w[ptr] = list_ptr(ptr, at, 5, v.len()); + } + Ptr::Bools(v) => { + w.extend(v.chunks(64).map(|c| c.iter().enumerate().fold(0u64, |acc, (i, &b)| acc | u64::from(b) << i))); + w[ptr] = list_ptr(ptr, at, 1, v.len()); + } + Ptr::Texts(v) => { + w.extend(std::iter::repeat_n(0, v.len())); + w[ptr] = list_ptr(ptr, at, 6, v.len()); + for (i, t) in v.iter().enumerate() { + place(w, at + i, &Ptr::Text(t.clone())); + } + } + } + // An empty list still has a pointer (so it reads as empty, not null). + if w[ptr] == 0 && !matches!(p, Ptr::Null) { + let size = match p { + Ptr::Structs { .. } => 7, + Ptr::U8s(_) | Ptr::Text(_) => 2, + Ptr::U16s(_) => 3, + Ptr::U32s(_) => 4, + Ptr::U64s(_) => 5, + Ptr::Bools(_) => 1, + _ => 6, + }; + w[ptr] = list_ptr(ptr, at, size, 0); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// A struct of every primitive, a nested struct, a composite list and + /// text, written and read back field for field. + #[test] + fn what_is_written_reads_back() { + let mut inner = Out::new(1, 1); + inner.set_u16(1, 0xbeef); + inner.ptrs[0] = Ptr::Text("héllo".into()); + let mut items = Vec::new(); + for k in 0..3u32 { + let mut e = Out::new(1, 1); + e.set_u32(0, 100 + k); + e.ptrs[0] = Ptr::U32s((0..k).collect()); + items.push(e); + } + let mut root = Out::new(2, 6); + root.set_u32(0, 7); + root.set_u16(2, 0x1234); + root.set_u8(6, 9); + root.set_bool(56, true); + root.set_f64(1, -0.1); + root.ptrs[0] = Ptr::Struct(Box::new(inner)); + root.ptrs[1] = Ptr::Structs { data_words: 1, ptr_count: 1, items }; + root.ptrs[2] = Ptr::Texts(vec!["a".into(), String::new(), "bcd".into()]); + root.ptrs[3] = Ptr::Bools(vec![true, false, true, true, false, false, false, false, true]); + root.ptrs[4] = Ptr::U16s(vec![]); + root.ptrs[5] = Ptr::U64s(vec![u64::MAX, 1]); + let bytes = write(&root); + assert_eq!(bytes.len() % 8, 0); + let m = Message::read(&bytes).unwrap(); + let r = m.root().unwrap(); + assert_eq!((r.u32(0), r.u16(2), r.u8(6), r.bool(56), r.bool(57)), (7, 0x1234, 9, true, false)); + assert_eq!(r.f64(1).to_bits(), (-0.1f64).to_bits()); + let i = r.get_struct(0).unwrap().unwrap(); + assert_eq!((i.u16(1), i.get_text(0).unwrap()), (0xbeef, "héllo".to_string())); + let l = r.get_list(1).unwrap().unwrap(); + assert_eq!(l.len(), 3); + for k in 0..3 { + let e = l.get_struct(k).unwrap(); + assert_eq!(e.u32(0), 100 + k as u32); + assert_eq!(e.get_list(0).unwrap().unwrap().u32s().unwrap(), (0..k as u32).collect::>()); + } + assert_eq!(r.get_list(2).unwrap().unwrap().texts().unwrap(), vec!["a", "", "bcd"]); + assert_eq!(r.get_list(3).unwrap().unwrap().bools().unwrap(), vec![true, false, true, true, false, false, false, false, true]); + let empty = r.get_list(4).unwrap().unwrap(); + assert!(empty.is_empty() && empty.u16s().unwrap().is_empty()); + assert_eq!(r.get_list(5).unwrap().unwrap().u64s().unwrap(), vec![u64::MAX, 1]); + // A field past the struct's sections reads as its default. + assert_eq!((r.u64(9), r.get_list(9).unwrap().is_none()), (0, true)); + assert_eq!(write(&root), bytes, "the same struct writes the same bytes"); + } + + /// A message whose pointers lead outside it, loop, or ask for more words + /// than it has is refused, never read past its end. + #[test] + fn malformed_messages_are_refused() { + assert!(Message::read(&[]).is_err()); + assert!(Message::read(&[0, 0, 0, 0, 5, 0, 0, 0]).is_err(), "a segment longer than the file"); + // A root pointing past its segment. + let mut b = vec![0u8, 0, 0, 0, 1, 0, 0, 0]; + b.extend_from_slice(&(struct_ptr(0, 10, 1, 0)).to_le_bytes()); + assert!(Message::read(&b).unwrap().root().is_err()); + // A root pointing at itself as a struct with a pointer: following it + // over and over runs into the depth limit, not forever. + let mut b = vec![0u8, 0, 0, 0, 2, 0, 0, 0]; + b.extend_from_slice(&struct_ptr(0, 1, 0, 1).to_le_bytes()); + b.extend_from_slice(&struct_ptr(1, 1, 0, 1).to_le_bytes()); + let m = Message::read(&b).unwrap(); + let mut s = m.root().unwrap(); + let mut steps = 0; + let err = loop { + match s.get_struct(0) { + Ok(Some(n)) => s = n, + Ok(None) => panic!("the loop ended"), + Err(e) => break e, + } + steps += 1; + }; + assert!(err.contains("deeper") && steps <= MAX_DEPTH as usize, "{err}"); + // A composite list whose tag claims more elements than its words hold. + let mut b = vec![0u8, 0, 0, 0, 4, 0, 0, 0]; + b.extend_from_slice(&struct_ptr(0, 1, 0, 1).to_le_bytes()); + b.extend_from_slice(&list_ptr(1, 2, 7, 1).to_le_bytes()); + b.extend_from_slice(&((5u64 << 2) | 1 << 32).to_le_bytes()); + b.extend_from_slice(&0u64.to_le_bytes()); + let m = Message::read(&b).unwrap(); + assert!(m.root().unwrap().get_list(0).is_err()); + // Text without its NUL. + let mut t = Out::new(0, 1); + t.ptrs[0] = Ptr::U8s(b"abc".to_vec()); + let bytes = write(&t); + assert!(Message::read(&bytes).unwrap().root().unwrap().get_text(0).is_err()); + } + + /// A far pointer and a double-far pointer to an object in another + /// segment read as a near one does. + #[test] + fn far_pointers_are_followed() { + // Segment 0: the root's far pointer. Segment 1: a landing pad and the struct. + let mut seg0 = vec![2u64 | (1u64 << 32)]; + let mut seg1 = vec![struct_ptr(0, 1, 1, 0), 0x2a]; + let mut b = vec![1u8, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]; + for w in seg0.drain(..).chain(seg1.drain(..)) { + b.extend_from_slice(&w.to_le_bytes()); + } + let m = Message::read(&b).unwrap(); + assert_eq!(m.root().unwrap().u64(0), 0x2a); + // Double far: segment 0 points at a two-word pad in segment 1, whose + // far pointer names the struct's place in segment 2 and whose tag its size. + let words: Vec> = vec![vec![2 | 4 | (1u64 << 32)], vec![2 | (2u64 << 32), 1u64 << 32], vec![0x55]]; + let mut b = vec![2u8, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0]; + for s in &words { + for w in s { + b.extend_from_slice(&w.to_le_bytes()); + } + } + let m = Message::read(&b).unwrap(); + assert_eq!(m.root().unwrap().u64(0), 0x55); + } +} diff --git a/wai/wai-quantum/src/engine/formats/emit.rs b/wai/wai-quantum/src/engine/formats/emit.rs new file mode 100644 index 000000000..f891a9232 --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/emit.rs @@ -0,0 +1,256 @@ +//! What a reader of another format gives: a circuit of standard gates, +//! measurements, resets and conditions on measured bits, written out as +//! OpenQASM 3 for the one reader every surface uses. Angles are written as +//! the shortest decimal that reads back as the same double, so a rotation +//! keeps its exact bits through the text. + +/// A condition on measured bits. +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum Cond { + Const(bool), + /// The bit, as last measured on the path that reaches the condition. + Bit(u32), + Not(Box), + And(Box, Box), + Or(Box, Box), +} + +impl Cond { + pub(crate) fn not(c: Cond) -> Cond { + match c { + Cond::Const(b) => Cond::Const(!b), + Cond::Not(x) => *x, + x => Cond::Not(Box::new(x)), + } + } + pub(crate) fn and(a: Cond, b: Cond) -> Cond { + match (a, b) { + (Cond::Const(false), _) | (_, Cond::Const(false)) => Cond::Const(false), + (Cond::Const(true), x) | (x, Cond::Const(true)) => x, + (a, b) => Cond::And(Box::new(a), Box::new(b)), + } + } + pub(crate) fn or(a: Cond, b: Cond) -> Cond { + match (a, b) { + (Cond::Const(true), _) | (_, Cond::Const(true)) => Cond::Const(true), + (Cond::Const(false), x) | (x, Cond::Const(false)) => x, + (a, b) => Cond::Or(Box::new(a), Box::new(b)), + } + } + pub(crate) fn xor(a: Cond, b: Cond) -> Cond { + Cond::or(Cond::and(a.clone(), Cond::not(b.clone())), Cond::and(Cond::not(a), b)) + } + /// Its terms. + pub(crate) fn size(&self) -> usize { + match self { + Cond::Const(_) | Cond::Bit(_) => 1, + Cond::Not(x) => 1 + x.size(), + Cond::And(a, b) | Cond::Or(a, b) => 1 + a.size() + b.size(), + } + } + + /// How deep its parentheses nest once written, a run of one operator + /// (`a && b && c`) written flat: what a reader of the text recurses on. + pub(crate) fn depth(&self) -> usize { + match self { + Cond::Const(_) | Cond::Bit(_) => 0, + Cond::Not(x) => 1 + x.depth(), + Cond::And(..) | Cond::Or(..) => { + let mut terms = Vec::new(); + self.flat(&mut terms); + 1 + terms.iter().map(|t| t.depth()).max().unwrap_or(0) + } + } + } + + /// The operands of a run of one operator, in order. + fn flat<'c>(&'c self, out: &mut Vec<&'c Cond>) { + match self { + Cond::And(a, b) | Cond::Or(a, b) => { + for t in [a, b] { + if std::mem::discriminant(&**t) == std::mem::discriminant(self) { + t.flat(out); + } else { + out.push(t); + } + } + } + x => out.push(x), + } + } + fn text(&self, s: &mut String) { + match self { + Cond::Const(b) => s.push_str(if *b { "true" } else { "false" }), + Cond::Bit(b) => s.push_str(&format!("c[{b}]")), + Cond::Not(x) => { + s.push_str("!("); + x.text(s); + s.push(')'); + } + Cond::And(..) | Cond::Or(..) => { + let mut terms = Vec::new(); + self.flat(&mut terms); + let op = if matches!(self, Cond::And(..)) { " && " } else { " || " }; + s.push('('); + for (i, t) in terms.iter().enumerate() { + if i > 0 { + s.push_str(op); + } + t.text(s); + } + s.push(')'); + } + } + } +} + +/// The most terms a condition may have. +pub(crate) const MAX_COND: usize = 1024; +/// The deepest a condition's parentheses may nest once written. +pub(crate) const MAX_COND_DEPTH: usize = 32; + +/// One statement. +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum Stmt { + /// A gate of the standard library (or `U`, `gphase`), with its + /// parameters, under `controls` (written first), inverted, and repeated + /// `pow` times (1 or more). + Gate { name: &'static str, params: Vec, controls: Vec, qubits: Vec, inv: bool, pow: u32 }, + Measure { qubit: u32, bit: u32 }, + Reset(u32), + If { cond: Cond, then: Vec, other: Vec }, +} + +impl Stmt { + pub(crate) fn gate(name: &'static str, params: Vec, qubits: Vec) -> Stmt { + Stmt::Gate { name, params, controls: Vec::new(), qubits, inv: false, pow: 1 } + } +} + +/// A circuit read from another format. +#[derive(Clone, Debug, Default, PartialEq)] +pub(crate) struct Circuit { + pub n_qubits: u32, + pub n_bits: u32, + pub body: Vec, +} + +/// A double as the shortest decimal that reads back as itself. +pub(crate) fn number(x: f64) -> String { + let s = format!("{x:?}"); + // `{:?}` writes `1e-7` and `1e300`, which OpenQASM reads; it writes no `inf` or `NaN` here. + debug_assert!(x.is_finite()); + s +} + +impl Circuit { + /// The circuit as an OpenQASM 3 program. + pub(crate) fn to_qasm(&self, header: &str) -> String { + let mut s = String::new(); + for line in header.lines() { + s.push_str("// "); + s.push_str(line); + s.push('\n'); + } + s.push_str("OPENQASM 3.0;\ninclude \"stdgates.inc\";\n"); + s.push_str(&format!("qubit[{}] q;\n", self.n_qubits)); + if self.n_bits > 0 { + s.push_str(&format!("bit[{}] c;\n", self.n_bits)); + } + write_body(&mut s, &self.body, 0); + s + } +} + +fn write_body(s: &mut String, body: &[Stmt], depth: usize) { + let pad = " ".repeat(depth); + for st in body { + s.push_str(&pad); + match st { + Stmt::Gate { name, params, controls, qubits, inv, pow } => { + match controls.len() { + 0 => {} + 1 => s.push_str("ctrl @ "), + k => s.push_str(&format!("ctrl({k}) @ ")), + } + if *inv { + s.push_str("inv @ "); + } + if *pow > 1 { + s.push_str(&format!("pow({pow}) @ ")); + } + s.push_str(name); + if !params.is_empty() { + s.push('('); + s.push_str(¶ms.iter().map(|&p| number(p)).collect::>().join(", ")); + s.push(')'); + } + let ops: Vec = controls.iter().chain(qubits).map(|q| format!("q[{q}]")).collect(); + if !ops.is_empty() { + s.push(' '); + s.push_str(&ops.join(", ")); + } + s.push_str(";\n"); + } + Stmt::Measure { qubit, bit } => s.push_str(&format!("c[{bit}] = measure q[{qubit}];\n")), + Stmt::Reset(q) => s.push_str(&format!("reset q[{q}];\n")), + Stmt::If { cond, then, other } => { + s.push_str("if ("); + cond.text(s); + s.push_str(") {\n"); + write_body(s, then, depth + 1); + s.push_str(&pad); + s.push('}'); + if !other.is_empty() { + s.push_str(" else {\n"); + write_body(s, other, depth + 1); + s.push_str(&pad); + s.push('}'); + } + s.push('\n'); + } + } + } +} + +/// Whether a condition is small and shallow enough to write and read back. +pub(crate) fn cond_fits(c: &Cond) -> bool { + c.size() <= MAX_COND && c.depth() <= MAX_COND_DEPTH +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn a_circuit_writes_as_openqasm_and_reads_back() { + let c = Circuit { + n_qubits: 3, + n_bits: 2, + body: vec![ + Stmt::gate("h", vec![], vec![0]), + Stmt::Gate { name: "rz", params: vec![0.1], controls: vec![0, 1], qubits: vec![2], inv: true, pow: 3 }, + Stmt::gate("gphase", vec![-1e-7], vec![]), + Stmt::Measure { qubit: 0, bit: 0 }, + Stmt::If { cond: Cond::xor(Cond::Bit(0), Cond::Const(true)), then: vec![Stmt::gate("x", vec![], vec![1])], other: vec![Stmt::Reset(2)] }, + Stmt::Measure { qubit: 1, bit: 1 }, + ], + }; + let text = c.to_qasm("read from a test"); + assert!(text.starts_with("// read from a test\nOPENQASM 3.0;"), "{text}"); + assert!(text.contains("ctrl(2) @ inv @ pow(3) @ rz(0.1) q[0], q[1], q[2];"), "{text}"); + assert!(text.contains("gphase(-1e-7);"), "{text}"); + assert!(text.contains("if (!(c[0])) {\n x q[1];\n} else {\n reset q[2];\n}"), "{text}"); + let p = crate::qasm::parse(&text).unwrap(); + assert_eq!((p.n_qubits, p.n_bits), (3, 2)); + assert!(p.dynamic.is_some()); + } + + #[test] + fn numbers_keep_their_bits() { + for x in [0.1, -0.5, std::f64::consts::PI, 1e-300, 123456789.0, -0.0, 5e-324] { + let t = number(x); + assert_eq!(t.parse::().unwrap().to_bits(), x.to_bits(), "{t}"); + } + } +} diff --git a/wai/wai-quantum/src/engine/formats/jeff.rs b/wai/wai-quantum/src/engine/formats/jeff.rs new file mode 100644 index 000000000..6d0abe3d6 --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/jeff.rs @@ -0,0 +1,1794 @@ +//! jeff, the open exchange format for quantum programs (format 0.3, its +//! Cap'n Proto schema `jeff.capnp`), read to a program and written from one. +//! +//! A jeff module is a list of functions, each a dataflow graph: operations +//! that consume values and produce new ones, a qubit used exactly once after +//! it is produced. Reading runs the entry point's graph in an order its +//! values allow and writes what it does as OpenQASM 3 for the one reader: +//! +//! - **Qubits**: each `alloc` is a new qubit `q[i]`, in the order the reader +//! reaches them; registers (`qureg`) of a size known on reading are their +//! qubits, extracted, inserted, sliced, split and joined by index. An entry +//! point that takes qubits takes them as the program's first qubits, from +//! `|0⟩`, and returns them unmeasured +//! ([`Q716`](crate::qasm::diag::JEFF_ENTRY_QUBITS)). +//! - **Gates**: the well-known gates, with their control qubits (given after +//! the targets, as the schema says), `adjoint` and `power` (0 and 1 both +//! apply the gate once) as `ctrl @`, `inv @` and `pow @`. A Pauli-product +//! rotation `exp(iθP)` is lowered exactly +//! ([`Q712`](crate::qasm::diag::JEFF_PPR)). A custom gate is read only when +//! its name is a gate of the standard library with its qubits and +//! parameters ([`Q711`](crate::qasm::diag::JEFF_CUSTOM_NAMED)); any other +//! is refused ([`Q710`](crate::qasm::diag::JEFF_CUSTOM)). +//! - **Measurement**: each `measure` and `measureNd` writes a new bit, in the +//! order the reader reaches them; when the entry point returns its bits +//! (one-bit integers, or an array of them), the program's bits are those, +//! in that order. +//! - **Classical values** known on reading are computed then, the same bits +//! on every platform (integers at their width; floats through the crate's +//! own functions). A bit from a measurement may be negated, combined with +//! `and`, `or`, `xor`, `eq` and `select`, and branched on. +//! - **Control flow** ([`Q715`](crate::qasm::diag::JEFF_STRUCTURED)): `for` +//! and `while` with what decides them known on reading are unrolled, a +//! `switch` on a known value decided, a `switch` on a measured bit written +//! as `if … else`, and a call of a function defined in the module inlined. +//! +//! Anything else is refused with its code, the function and the operation. +//! +//! **Writing** gives a module of one function, `main`: every qubit +//! allocated, each gate as a well-known gate (controlled gates with their +//! control qubits, `rxx`, `ryy`, `rzz` as Pauli-product rotations), exact +//! including the global phase, and the measurements written into a bit array +//! that `main` returns, its index each measurement's bit. A program that +//! branches on what it measured is refused ([`Q720`](crate::qasm::diag::WRITE_CANNOT)). + +use super::capnp::{Message, Out, Ptr, Struct}; +use super::emit::{self, Circuit, Cond, Stmt}; +use crate::qasm::diag::{self, Diagnostic}; +use crate::qasm::{Instr, Kind, Program}; +use serde_json::{Value, json}; +use std::collections::BTreeMap; +use std::rc::Rc; + +/// The format version written. +pub const VERSION: (u32, u32, u32) = (0, 3, 1); + +/// What a module was read as. +#[derive(Clone, Debug, PartialEq)] +pub struct Reading { + /// The program, as OpenQASM 3. + pub qasm: String, + /// The warnings and notes, each with its code; where they stand is in + /// the message. + pub notes: Vec, + /// The version, the tool that wrote it, the entry point, its qubits and + /// bits. + pub summary: Value, +} + +fn unread(msg: impl Into) -> Diagnostic { + Diagnostic::new(diag::FORMAT_UNREAD, None, msg) +} + +// --------------------------------------------------------------------------- +// The schema, decoded +// --------------------------------------------------------------------------- + +#[derive(Clone, Debug, PartialEq)] +enum Ty { + Qubit, + Qureg(Option), + Int(u8), + IntArray(u8, Option), + Float(u16), + FloatArray(u16, Option), +} + +#[derive(Clone, Debug)] +struct Region { + sources: Vec, + targets: Vec, + ops: Vec, +} + +#[derive(Clone, Debug)] +struct Op { + inputs: Vec, + outputs: Vec, + ins: Ins, +} + +#[derive(Clone, Debug)] +enum GateKind { + WellKnown(u16), + Custom { name: u16, qubits: u8, params: u8 }, + Ppr(Vec), +} + +#[derive(Clone, Debug)] +struct Gate { + kind: GateKind, + controls: u8, + adjoint: bool, + power: u8, +} + +#[derive(Clone, Debug)] +enum Ins { + Qubit(u16, Option), + Qureg(u16), + Int(u16, u64), + IntArray(u16, Vec, u8), + Float(u16, f64), + FloatArray(u16, Vec), + Switch(Vec, Option>), + For(Box), + While(Box, Box), + Call(u16), +} + +#[derive(Clone, Debug)] +enum Func { + Definition { body: Region, values: Vec }, + Declaration, +} + +struct Module { + version: (u32, u32, u32), + tool: String, + tool_version: String, + functions: Vec<(u16, Func)>, + strings: Vec, + entry: u16, +} + +fn list_u32(s: &Struct, i: usize) -> Result, String> { + Ok(match s.get_list(i)? { + Some(l) => l.u32s()?, + None => Vec::new(), + }) +} + +fn structs<'a>(s: &Struct<'a>, i: usize) -> Result>, String> { + match s.get_list(i)? { + Some(l) => (0..l.len()).map(|k| l.get_struct(k)).collect(), + None => Ok(Vec::new()), + } +} + +fn ty(s: &Struct) -> Result { + let len = |disc_at: usize, val_at: usize| match s.u16(disc_at) { + 0 => Ok(None), + 1 => Ok(Some(s.u32(val_at))), + d => Err(format!("a length of kind {d}, which the schema does not have")), + }; + Ok(match s.u16(0) { + 0 => Ty::Qubit, + 1 => Ty::Qureg(len(1, 1)?), + 2 => Ty::Int(s.u8(2)), + 3 => Ty::IntArray(s.u8(2), len(2, 2)?), + 4 => Ty::Float(s.u16(1)), + 5 => Ty::FloatArray(s.u16(1), len(2, 2)?), + d => return Err(format!("a type of kind {d}, which the schema does not have")), + }) +} + +fn values(s: &Struct, i: usize) -> Result, String> { + structs(s, i)?.iter().map(|v| v.get_struct(0)?.ok_or_else(|| "a value with no type".to_string()).and_then(|t| ty(&t))).collect() +} + +fn region(s: &Struct) -> Result { + Ok(Region { sources: list_u32(s, 0)?, targets: list_u32(s, 1)?, ops: structs(s, 2)?.iter().map(op).collect::>()? }) +} + +fn region_at(s: &Struct, i: usize) -> Result, String> { + s.get_struct(i)?.map(|r| region(&r)).transpose() +} + +fn op(s: &Struct) -> Result { + let inputs = list_u32(s, 0)?; + let outputs = list_u32(s, 1)?; + let body = s.get_struct(3)?; + fn need(b: Option>) -> Result, String> { + b.ok_or_else(|| "an operation with no instruction".to_string()) + } + let ins = match s.u16(0) { + 0 => { + let q = need(body)?; + let d = q.u16(0); + let gate = if d == 6 { + let g = q.get_struct(0)?.ok_or("a gate with no gate")?; + let kind = match g.u16(1) { + 0 => GateKind::WellKnown(g.u16(0)), + 1 => GateKind::Custom { name: g.u16(0), qubits: g.u8(4), params: g.u8(5) }, + 2 => GateKind::Ppr(match g.get_list(0)? { + Some(l) => l.u16s()?, + None => Vec::new(), + }), + k => return Err(format!("a gate of kind {k}, which the schema does not have")), + }; + Some(Gate { kind, controls: g.u8(6), adjoint: g.bool(56), power: g.u8(8) }) + } else { + None + }; + Ins::Qubit(d, gate) + } + 1 => Ins::Qureg(need(body)?.u16(0)), + 2 => { + let i = need(body)?; + let d = i.u16(1); + let v = match d { + 0 => u64::from(i.bool(0)), + 1 => u64::from(i.u8(0)), + 2 => u64::from(i.u16(0)), + 3 => u64::from(i.u32(1)), + 4 => i.u64(1), + _ => 0, + }; + Ins::Int(d, v) + } + 3 => { + let a = need(body)?; + let d = a.u16(0); + let consts = match (d, a.get_list(0)?) { + (0, Some(l)) => l.bools()?.into_iter().map(u64::from).collect(), + (1, Some(l)) => l.u8s()?.into_iter().map(u64::from).collect(), + (2, Some(l)) => l.u16s()?.into_iter().map(u64::from).collect(), + (3, Some(l)) => l.u32s()?.into_iter().map(u64::from).collect(), + (4, Some(l)) => l.u64s()?, + _ => Vec::new(), + }; + Ins::IntArray(d, consts, a.u8(2)) + } + 4 => { + let f = need(body)?; + let d = f.u16(2); + let v = match d { + 0 => f64::from(f.f32(0)), + 1 => f.f64(1), + _ => 0.0, + }; + Ins::Float(d, v) + } + 5 => { + let f = need(body)?; + let d = f.u16(0); + let consts = match (d, f.get_list(0)?) { + (0, Some(l)) => l.u32s()?.into_iter().map(|b| f64::from(f32::from_bits(b))).collect(), + (1, Some(l)) => l.u64s()?.into_iter().map(f64::from_bits).collect(), + _ => Vec::new(), + }; + Ins::FloatArray(d, consts) + } + 6 => { + let c = need(body)?; + match c.u16(0) { + 0 => Ins::Switch(structs(&c, 0)?.iter().map(region).collect::>()?, region_at(&c, 1)?.map(Box::new)), + 1 => Ins::For(Box::new(region_at(&c, 0)?.ok_or("a `for` with no body")?)), + 2 => Ins::While(Box::new(region_at(&c, 0)?.ok_or("a `while` with no `before` region")?), Box::new(region_at(&c, 1)?.ok_or("a `while` with no `after` region")?)), + k => return Err(format!("a control-flow operation of kind {k}, which the schema does not have")), + } + } + 7 => Ins::Call(need(body)?.u16(0)), + k => return Err(format!("an instruction of kind {k}, which the schema does not have")), + }; + Ok(Op { inputs, outputs, ins }) +} + +fn decode(bytes: &[u8]) -> Result { + let m = Message::read(bytes)?; + let root = m.root()?; + let mut functions = Vec::new(); + for f in structs(&root, 0)? { + let func = match f.u16(1) { + 0 => { + let body = region_at(&f, 0)?.unwrap_or(Region { sources: vec![], targets: vec![], ops: vec![] }); + let values = values(&f, 1)?; + if values.len() > MAX_VALUES { + return Err(format!("a function of {} values, more than the reader takes ({MAX_VALUES})", values.len())); + } + Func::Definition { body, values } + } + 1 => Func::Declaration, + k => return Err(format!("a function of kind {k}, which the schema does not have")), + }; + functions.push((f.u16(0), func)); + } + let strings = match root.get_list(1)? { + Some(l) => l.texts()?, + None => Vec::new(), + }; + Ok(Module { version: (root.u32(0), root.u32(2), root.u32(3)), tool: root.get_text(3)?, tool_version: root.get_text(4)?, functions, strings, entry: root.u16(2) }) +} + +// --------------------------------------------------------------------------- +// Running the graph +// --------------------------------------------------------------------------- + +/// A value as the reader holds it. +#[derive(Clone, Debug, PartialEq)] +enum V { + Qubit(u32), + Qureg(Vec>), + /// An integer of a width: known on reading, or (a one-bit integer) a + /// condition on measured bits. + Int(u8, IntV), + IntArray(u8, Vec), + Float(f64), + FloatArray(Vec), + /// Used already (a qubit or a register). + Used, + /// What the two sides of a branch leave different, and is not a bit. + Unknown(String), +} + +#[derive(Clone, Debug, PartialEq)] +enum IntV { + Known(u64), + /// Shared, so an array copied by `setIndex` copies no condition. + Cond(Rc), +} + +/// What a value holds, in the units the reader's budget counts: one, and +/// one more for each slot of a register or element of an array. +fn weight(v: &V) -> usize { + 1 + match v { + V::Qureg(r) => r.len(), + V::IntArray(_, a) => a.len(), + V::FloatArray(a) => a.len(), + _ => 0, + } +} + +const WELL_KNOWN: [(&str, &str, usize, usize); 14] = [ + // (jeff name, OpenQASM gate, qubits, parameters), by enum value. + ("x", "x", 1, 0), + ("y", "y", 1, 0), + ("z", "z", 1, 0), + ("s", "s", 1, 0), + ("t", "t", 1, 0), + ("r1", "p", 1, 1), + ("rx", "rx", 1, 1), + ("ry", "ry", 1, 1), + ("rz", "rz", 1, 1), + ("h", "h", 1, 0), + ("u", "U", 1, 3), + ("swap", "swap", 2, 0), + ("i", "id", 1, 0), + ("gphase", "gphase", 0, 1), +]; + +/// The OpenQASM names a custom gate may carry, any case, to be read as that gate. +const STANDARD: &[&str] = &[ + "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "sx", "sxdg", "rx", "ry", "rz", "p", "phase", "u1", "u2", "u3", "u", "cx", "cnot", "cy", "cz", "ch", "cp", "cphase", "cu1", "crx", "cry", "crz", "cu3", "cu", "swap", "iswap", "rxx", "ryy", "rzz", "ccx", "toffoli", "ccz", "cswap", "fredkin", +]; + +/// The largest module read, in bytes. +pub const MAX_BYTES: usize = 16 << 20; +/// The most operations run, unrolled loops and inlined calls included. +pub const MAX_STEPS: usize = 1 << 22; +/// The most gates, measurements and resets written. +pub const MAX_WRITTEN: usize = 1 << 20; +/// The most elements one classical array holds. +pub const MAX_ARRAY: usize = 1 << 16; +/// The most values one function declares. +pub const MAX_VALUES: usize = 1 << 20; +/// What the reader may hold, in all, over a module's run: every qubit, every +/// register slot and array element made or copied, every value of every +/// call's frame and every term of every condition built count against it, +/// so no module makes the reader hold more than a bounded amount, whatever +/// its sizes say. +pub const MAX_UNITS: usize = 1 << 22; +/// The deepest calls and regions are nested. +const MAX_NESTING: u32 = 32; + +struct Runner<'m> { + m: &'m Module, + n_qubits: u32, + n_bits: u32, + steps: usize, + notes: BTreeMap<&'static str, (usize, String)>, + custom: BTreeMap, + ppr: usize, + arbitrary: usize, + /// Gates, measurements and resets written so far. + emitted: usize, + /// What the run has made so far, against [`MAX_UNITS`]. + units: usize, +} + +/// A function's values as it runs. +struct Frame<'m> { + f: u16, + values: &'m [Ty], + vals: Vec>, +} + +impl<'m> Runner<'m> { + fn name(&self, f: u16) -> String { + match self.m.functions.get(f as usize) { + Some((n, _)) => self.m.strings.get(*n as usize).cloned().unwrap_or_else(|| format!("#{f}")), + None => format!("#{f}"), + } + } + + fn at(&self, fr: &Frame, k: usize) -> String { + format!("in function `{}`, operation {k}", self.name(fr.f)) + } + + fn note(&mut self, code: &'static str, what: String) { + self.notes.entry(code).or_insert((0, what)).0 += 1; + } + + fn take(&self, fr: &mut Frame, v: u32, at: &str) -> Result { + let slot = fr.vals.get_mut(v as usize).ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {v} is outside the function's {} values", fr.values.len())))?; + let x = slot.clone().ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {v} is used before it is defined")))?; + match x { + V::Used => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {v} (a qubit or register) is used a second time"))), + V::Qubit(_) | V::Qureg(_) => { + *slot = Some(V::Used); + Ok(x) + } + V::Unknown(why) => Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: value {v} is not known on reading: {why}"))), + other => Ok(other), + } + } + + fn put(&self, fr: &mut Frame, v: u32, x: V, at: &str) -> Result<(), Diagnostic> { + let ty = fr.values.get(v as usize).ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {v} is outside the function's {} values", fr.values.len())))?; + let fits = match (&x, ty) { + (V::Qubit(_), Ty::Qubit) | (V::Qureg(_), Ty::Qureg(_)) | (V::Float(_), Ty::Float(_)) | (V::FloatArray(_), Ty::FloatArray(..)) | (V::Unknown(_), _) => true, + (V::Int(w, _), Ty::Int(t)) => w == t, + (V::IntArray(w, _), Ty::IntArray(t, _)) => w == t, + _ => false, + }; + if !fits { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {v} is declared {ty:?}, and the operation gives it something else"))); + } + fr.vals[v as usize] = Some(x); + Ok(()) + } + + fn qubit(&mut self, x: V, at: &str) -> Result { + match x { + V::Qubit(q) => Ok(q), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a qubit is expected"))), + } + } + + fn known_int(&self, x: &V, at: &str, what: &str) -> Result { + match x { + V::Int(_, IntV::Known(v)) => Ok(*v), + V::Int(_, IntV::Cond(_)) => Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: {what} depends on a measurement"))), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: {what} is not an integer"))), + } + } + + fn float(&self, x: &V, at: &str) -> Result { + match x { + V::Float(f) if f.is_finite() => Ok(*f), + V::Float(_) => Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an angle that is not finite"))), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a float is expected"))), + } + } + + fn fresh_qubit(&mut self, at: &str) -> Result { + if self.n_qubits >= crate::qasm::MAX_QUBITS { + return Err(Diagnostic::new(diag::TOO_MANY_QUBITS, None, format!("{at}: more qubits than the reader takes ({})", crate::qasm::MAX_QUBITS))); + } + self.n_qubits += 1; + Ok(self.n_qubits - 1) + } + + /// One more gate, measurement or reset written, within what the reader holds. + fn wrote(&mut self, at: &str) -> Result<(), Diagnostic> { + self.emitted += 1; + if self.emitted > MAX_WRITTEN { + return Err(Diagnostic::new(diag::TOO_MANY_GATES, None, format!("{at}: the module runs to more than the {MAX_WRITTEN} gates the reader writes"))); + } + Ok(()) + } + + /// `n` more units made, within [`MAX_UNITS`]: checked before what they + /// count is allocated. + fn charge(&mut self, n: usize, at: &str) -> Result<(), Diagnostic> { + self.units = self.units.saturating_add(n); + if self.units > MAX_UNITS { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: the module makes more registers, arrays, values and conditions than the reader holds ({MAX_UNITS} in all)"))); + } + Ok(()) + } + + /// A condition built from measured bits, within the reader's bounds on + /// one condition and on all of them. + fn cond(&mut self, c: Cond, at: &str) -> Result { + if !emit::cond_fits(&c) { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a condition of more than {} terms or nested deeper than {}", emit::MAX_COND, emit::MAX_COND_DEPTH))); + } + self.charge(c.size(), at)?; + Ok(IntV::Cond(Rc::new(c))) + } + + /// A register of `k` new qubits, or the refusal when they are more than + /// the reader takes: checked before the register is made. + fn fresh_register(&mut self, k: usize, at: &str) -> Result>, Diagnostic> { + if k > (crate::qasm::MAX_QUBITS - self.n_qubits) as usize { + return Err(Diagnostic::new(diag::TOO_MANY_QUBITS, None, format!("{at}: a register of {k} qubits, beside {} already, is more than the reader takes ({})", self.n_qubits, crate::qasm::MAX_QUBITS))); + } + self.charge(k, at)?; + let mut r = Vec::with_capacity(k); + for _ in 0..k { + r.push(Some(self.fresh_qubit(at)?)); + } + Ok(r) + } + + /// An array of `n` elements, or the refusal when it is longer than the + /// reader holds: checked before the array is made. + fn array_len(&mut self, n: usize, at: &str) -> Result { + if n > MAX_ARRAY { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an array of {n} elements, more than the reader holds ({MAX_ARRAY})"))); + } + self.charge(n, at)?; + Ok(n) + } + + fn step(&mut self, at: &str) -> Result<(), Diagnostic> { + self.steps += 1; + if self.steps > MAX_STEPS { + return Err(Diagnostic::new(diag::UNROLL_LIMIT, None, format!("{at}: the module runs to more than the {MAX_STEPS} operations the reader runs"))); + } + Ok(()) + } + + /// Runs `r` with its sources bound to `args`, and gives its targets. + fn region(&mut self, fr: &mut Frame, r: &Region, args: Vec, out: &mut Vec, depth: u32) -> Result, Diagnostic> { + if depth > MAX_NESTING { + return Err(Diagnostic::new(diag::TOO_DEEP, None, format!("in function `{}`: regions and calls nested deeper than {MAX_NESTING}", self.name(fr.f)))); + } + if args.len() != r.sources.len() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("in function `{}`: a region takes {} values and is given {}", self.name(fr.f), r.sources.len(), args.len()))); + } + // The region's own values are defined afresh each time it runs. + for o in &r.ops { + for &v in &o.outputs { + if let Some(s) = fr.vals.get_mut(v as usize) { + *s = None; + } + } + } + for (&s, a) in r.sources.iter().zip(args) { + let at = format!("in function `{}`, a region's source", self.name(fr.f)); + self.put(fr, s, a, &at)?; + } + for k in order(r, fr.vals.len()).map_err(|v| Diagnostic::new(diag::JEFF_LINEAR, None, format!("in function `{}`: {v}", self.name(fr.f))))? { + self.op(fr, &r.ops[k], k, out, depth)?; + } + let at = format!("in function `{}`, a region's targets", self.name(fr.f)); + r.targets.iter().map(|&t| self.take(fr, t, &at)).collect() + } + + fn op(&mut self, fr: &mut Frame, o: &Op, k: usize, out: &mut Vec, depth: u32) -> Result<(), Diagnostic> { + let at = self.at(fr, k); + self.step(&at)?; + let ins: Vec = match &o.ins { + // A region's inputs are taken by the control flow below. + Ins::Switch(..) | Ins::For(_) | Ins::While(..) | Ins::Call(_) => Vec::new(), + _ => o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?, + }; + let outs: Vec = match &o.ins { + Ins::Qubit(d, gate) => self.qubit_op(*d, gate.as_ref(), ins, out, &at)?, + Ins::Qureg(d) => self.qureg_op(*d, ins, &at)?, + Ins::Int(d, c) => vec![self.int_op(fr, o, *d, *c, &ins, &at)?], + Ins::IntArray(d, consts, w) => vec![self.int_array_op(fr, o, *d, consts, *w, ins, &at)?], + Ins::Float(d, c) => { + let w = match o.outputs.first().and_then(|&v| fr.values.get(v as usize)) { + Some(Ty::Int(w)) => Some(*w), + _ => None, + }; + vec![self.float_op(*d, *c, &ins, w, &at)?] + } + Ins::FloatArray(d, consts) => vec![self.float_array_op(*d, consts, ins, &at)?], + Ins::Switch(branches, default) => { + let mut args: Vec = o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?; + if args.is_empty() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `switch` with nothing to switch on"))); + } + let sel = args.remove(0); + self.note(diag::JEFF_STRUCTURED, "structured control flow decided on reading, unrolled, or written as conditions".into()); + match sel { + V::Int(_, IntV::Known(v)) => { + let r = usize::try_from(v).ok().and_then(|i| branches.get(i)).or(default.as_deref()).ok_or_else(|| Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `switch` on {v} with no such branch and no default")))?; + self.region(fr, r, args, out, depth + 1)? + } + V::Int(1, IntV::Cond(c)) => { + let zero = branches.first().ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `switch` with no branches")))?; + let one = branches.get(1).or(default.as_deref()).ok_or_else(|| Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `switch` on a measured bit with no branch for 1")))?; + // Each side takes its own copy of what the switch passes it. + self.charge(args.iter().map(weight).sum(), &at)?; + let (mut then, mut other) = (Vec::new(), Vec::new()); + let a = self.region(fr, one, args.clone(), &mut then, depth + 1)?; + let b = self.region(fr, zero, args, &mut other, depth + 1)?; + if a.len() != b.len() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `switch` whose branches give different numbers of values"))); + } + let mut merged = Vec::with_capacity(a.len()); + for (x, y) in a.into_iter().zip(b) { + merged.push(self.join(&c, x, y, &at)?); + } + out.push(Stmt::If { cond: (*c).clone(), then, other }); + merged + } + _ => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `switch` on a value that is neither known on reading nor a measured bit"))), + } + } + Ins::For(body) => { + let mut args: Vec = o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?; + if args.len() < 3 { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `for` takes a start, a stop and a step"))); + } + let state = args.split_off(3); + let w = match &args[0] { + V::Int(w, _) => *w, + _ => 64, + }; + let sign = |v: u64| -> i128 { if w >= 64 || w == 0 { v as i64 as i128 } else { ((v << (64 - w)) as i64 >> (64 - w)) as i128 } }; + let start = sign(self.known_int(&args[0], &at, "a loop's start")?); + let stop = sign(self.known_int(&args[1], &at, "a loop's stop")?); + let step = sign(self.known_int(&args[2], &at, "a loop's step")?); + if step == 0 { + return Err(Diagnostic::new(diag::BAD_RANGE, None, format!("{at}: a `for` with a step of 0"))); + } + self.note(diag::JEFF_STRUCTURED, "structured control flow decided on reading, unrolled, or written as conditions".into()); + let mut state = state; + let mut i = start; + while (step > 0 && i < stop) || (step < 0 && i > stop) { + self.step(&at)?; + let mut a = vec![V::Int(w, IntV::Known(i as u64 & mask(w)))]; + a.extend(state); + state = self.region(fr, body, a, out, depth + 1)?; + i += step; + } + state + } + Ins::While(before, after) => { + let mut state: Vec = o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?; + self.note(diag::JEFF_STRUCTURED, "structured control flow decided on reading, unrolled, or written as conditions".into()); + loop { + self.step(&at)?; + let mut r = self.region(fr, before, state, out, depth + 1)?; + if r.is_empty() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a `while` whose `before` region gives no condition"))); + } + let c = r.remove(0); + match c { + V::Int(_, IntV::Known(0)) => break r, + V::Int(_, IntV::Known(_)) => state = self.region(fr, after, r, out, depth + 1)?, + _ => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a `while` whose condition depends on a measurement"))), + } + } + } + Ins::Call(f) => { + let args: Vec = o.inputs.iter().map(|&v| self.take(fr, v, &at)).collect::>()?; + self.note(diag::JEFF_STRUCTURED, "structured control flow decided on reading, unrolled, or written as conditions".into()); + self.call(*f, args, out, depth + 1, &at)? + } + }; + if outs.len() != o.outputs.len() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: the operation gives {} values and names {}", outs.len(), o.outputs.len()))); + } + for (&v, x) in o.outputs.iter().zip(outs) { + self.put(fr, v, x, &at)?; + } + Ok(()) + } + + fn call(&mut self, f: u16, args: Vec, out: &mut Vec, depth: u32, at: &str) -> Result, Diagnostic> { + if depth > MAX_NESTING { + return Err(Diagnostic::new(diag::TOO_DEEP, None, format!("{at}: calls nested deeper than {MAX_NESTING}"))); + } + let m = self.m; + match m.functions.get(f as usize) { + Some((_, Func::Definition { body, values })) => { + // The callee's values, made for this call, count. + self.charge(values.len(), at)?; + let mut fr = Frame { f, values, vals: vec![None; values.len()] }; + self.region(&mut fr, body, args, out, depth) + } + Some((_, Func::Declaration)) => Err(Diagnostic::new(diag::INSTRUCTION_UNKNOWN, None, format!("{at}: a call of `{}`, which the module declares and does not define", self.name(f)))), + None => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a call of function {f}, which the module does not have"))), + } + } + + fn qubit_op(&mut self, d: u16, gate: Option<&Gate>, ins: Vec, out: &mut Vec, at: &str) -> Result, Diagnostic> { + let arity = |n: usize| -> Result<(), Diagnostic> { + if ins.len() != n { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: the operation takes {n} values and is given {}", ins.len()))); + } + Ok(()) + }; + Ok(match d { + 0 => { + arity(0)?; + vec![V::Qubit(self.fresh_qubit(at)?)] + } + 1 | 2 => { + arity(1)?; + self.qubit(ins.into_iter().next().expect("one"), at)?; + vec![] + } + 3 | 4 => { + arity(1)?; + let q = self.qubit(ins.into_iter().next().expect("one"), at)?; + let b = self.n_bits; + self.n_bits += 1; + self.wrote(at)?; + out.push(Stmt::Measure { qubit: q, bit: b }); + let bit = V::Int(1, IntV::Cond(Rc::new(Cond::Bit(b)))); + if d == 3 { vec![bit] } else { vec![V::Qubit(q), bit] } + } + 5 => { + arity(1)?; + let q = self.qubit(ins.into_iter().next().expect("one"), at)?; + self.wrote(at)?; + out.push(Stmt::Reset(q)); + vec![V::Qubit(q)] + } + 6 => self.gate(gate.ok_or_else(|| unread(format!("{at}: a gate with no gate")))?, ins, out, at)?, + k => return Err(unread(format!("{at}: a qubit operation of kind {k}, which the schema does not have"))), + }) + } + + fn gate(&mut self, g: &Gate, ins: Vec, out: &mut Vec, at: &str) -> Result, Diagnostic> { + let (nq, np) = match &g.kind { + GateKind::WellKnown(w) => { + let e = WELL_KNOWN.get(*w as usize).ok_or_else(|| unread(format!("{at}: well-known gate {w}, which the schema does not have")))?; + (e.2, e.3) + } + GateKind::Custom { qubits, params, .. } => (*qubits as usize, *params as usize), + GateKind::Ppr(p) => (p.len(), 1), + }; + let nc = g.controls as usize; + if ins.len() != nq + nc + np { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: the gate takes {} qubits, {nc} controls and {np} parameters, and is given {} values", nq, ins.len()))); + } + let mut it = ins.into_iter(); + let mut targets = Vec::with_capacity(nq); + for _ in 0..nq { + targets.push(self.qubit(it.next().expect("counted"), at)?); + } + let mut controls = Vec::with_capacity(nc); + for _ in 0..nc { + controls.push(self.qubit(it.next().expect("counted"), at)?); + } + let params: Vec = it.map(|x| self.float(&x, at)).collect::>()?; + let mut all = targets.clone(); + all.extend(&controls); + let mut seen = all.clone(); + seen.sort_unstable(); + seen.dedup(); + if seen.len() != all.len() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: one qubit given to a gate twice"))); + } + let pow = u32::from(g.power.max(1)); + self.wrote(at)?; + for &p in ¶ms { + if crate::qasm::dyadic(p).is_none() { + self.arbitrary += 1; + } + } + match &g.kind { + GateKind::WellKnown(w) => { + let name = WELL_KNOWN[*w as usize].1; + out.push(Stmt::Gate { name, params, controls, qubits: targets.clone(), inv: g.adjoint, pow }); + } + GateKind::Custom { name, .. } => { + let text = self.m.strings.get(*name as usize).cloned().ok_or_else(|| unread(format!("{at}: a custom gate named by string {name}, which the module does not have")))?; + let lower = text.to_ascii_lowercase(); + let known = STANDARD.iter().find(|&&s| s == lower).and_then(|&s| crate::qasm::builtin(s).map(|(kind, given)| (s, kind, given))); + let Some((std, _, _)) = known.filter(|(_, kind, given)| kind.arity().1 == nq && *given == np) else { + return Err(Diagnostic::new(diag::JEFF_CUSTOM, None, format!("{at}: the custom gate `{text}` ({nq} qubits, {np} parameters) is not a gate of the standard library with those qubits and parameters"))); + }; + *self.custom.entry(text).or_insert(0) += 1; + if controls.is_empty() && !g.adjoint && pow == 1 { + out.extend(super::qir::portable(std, ¶ms, &targets, false)); + } else { + out.push(Stmt::Gate { name: std, params, controls, qubits: targets.clone(), inv: g.adjoint, pow }); + } + } + GateKind::Ppr(paulis) => { + self.ppr += 1; + let mut theta = params[0] * f64::from(pow); + if g.adjoint { + theta = -theta; + } + let mut on = Vec::new(); + for (k, &p) in paulis.iter().enumerate() { + match p { + 0 => {} + 1..=3 => on.push((targets[k], p)), + _ => return Err(unread(format!("{at}: Pauli {p}, which the schema does not have"))), + } + } + if on.is_empty() { + out.push(Stmt::Gate { name: "gphase", params: vec![theta], controls, qubits: vec![], inv: false, pow: 1 }); + } else { + let g1 = |n: &'static str, q: u32| Stmt::gate(n, vec![], vec![q]); + let mut pre = Vec::new(); + let mut post = Vec::new(); + for &(q, p) in &on { + match p { + 1 => { + pre.push(g1("h", q)); + post.push(g1("h", q)); + } + 2 => { + pre.push(g1("sdg", q)); + pre.push(g1("h", q)); + post.push(g1("s", q)); + post.push(g1("h", q)); + } + _ => {} + } + } + let last = on.last().expect("one").0; + let ladder: Vec = on[..on.len() - 1].iter().map(|&(q, _)| Stmt::gate("cx", vec![], vec![q, last])).collect(); + out.extend(pre); + out.extend(ladder.iter().cloned()); + out.push(Stmt::Gate { name: "rz", params: vec![-2.0 * theta], controls, qubits: vec![last], inv: false, pow: 1 }); + out.extend(ladder.into_iter().rev()); + out.extend(post.into_iter().rev()); + } + } + } + Ok(all.into_iter().map(V::Qubit).collect()) + } + + fn qureg_op(&mut self, d: u16, ins: Vec, at: &str) -> Result, Diagnostic> { + let reg = |x: &V| -> Result>, Diagnostic> { + match x { + V::Qureg(r) => Ok(r.clone()), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a qubit register is expected"))), + } + }; + // A size or an index as it is: one past what any register holds is refused below, never cut to fit. + let n = |r: &Self, x: &V, what: &str| -> Result { r.known_int(x, at, what).map(|v| usize::try_from(v).unwrap_or(usize::MAX)) }; + let want = |k: usize| -> Result<(), Diagnostic> { + if ins.len() != k { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: the register operation takes {k} values and is given {}", ins.len()))); + } + Ok(()) + }; + let slot = |r: &[Option], i: usize| -> Result<(), Diagnostic> { + if i >= r.len() { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of a register of {}", r.len()))); + } + Ok(()) + }; + Ok(match d { + 0 => { + want(1)?; + let k = n(self, &ins[0], "a register's size")?; + vec![V::Qureg(self.fresh_register(k, at)?)] + } + 1 | 10 => { + want(1)?; + reg(&ins[0])?; + vec![] + } + 2 => { + want(2)?; + let mut r = reg(&ins[0])?; + let i = n(self, &ins[1], "an index")?; + slot(&r, i)?; + let q = r[i].take().ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: slot {i} of the register is empty")))?; + vec![V::Qureg(r), V::Qubit(q)] + } + 3 => { + want(3)?; + let mut r = reg(&ins[0])?; + let i = n(self, &ins[1], "an index")?; + let q = self.qubit(ins[2].clone(), at)?; + slot(&r, i)?; + if r[i].is_some() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: slot {i} of the register is filled already"))); + } + r[i] = Some(q); + vec![V::Qureg(r)] + } + 4 => { + want(3)?; + let mut r = reg(&ins[0])?; + let (s, l) = (n(self, &ins[1], "a slice's start")?, n(self, &ins[2], "a slice's length")?); + if s.checked_add(l).is_none_or(|e| e > r.len()) { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: a slice [{s}, {s}+{l}) of a register of {}", r.len()))); + } + let slice: Vec> = r[s..s + l].iter_mut().map(Option::take).collect(); + vec![V::Qureg(r), V::Qureg(slice)] + } + 5 => { + want(3)?; + let mut r = reg(&ins[0])?; + let s = n(self, &ins[1], "a slice's start")?; + let slice = reg(&ins[2])?; + if s.checked_add(slice.len()).is_none_or(|e| e > r.len()) { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: a slice of {} at {s} in a register of {}", slice.len(), r.len()))); + } + for (k, q) in slice.into_iter().enumerate() { + if q.is_some() { + if r[s + k].is_some() { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: slot {} of the register is filled already", s + k))); + } + r[s + k] = q; + } + } + vec![V::Qureg(r)] + } + 6 => { + want(1)?; + let r = reg(&ins[0])?; + let l = r.len() as u64; + vec![V::Qureg(r), V::Int(32, IntV::Known(l))] + } + 7 => { + want(2)?; + let mut r = reg(&ins[0])?; + let i = n(self, &ins[1], "an index")?; + if i > r.len() { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: a split at {i} of a register of {}", r.len()))); + } + let after = r.split_off(i); + vec![V::Qureg(r), V::Qureg(after)] + } + 8 => { + want(2)?; + let mut a = reg(&ins[0])?; + a.extend(reg(&ins[1])?); + vec![V::Qureg(a)] + } + 9 => { + let qs = ins.into_iter().map(|x| self.qubit(x, at).map(Some)).collect::, _>>()?; + vec![V::Qureg(qs)] + } + k => return Err(unread(format!("{at}: a register operation of kind {k}, which the schema does not have"))), + }) + } + + fn int_op(&mut self, fr: &Frame, o: &Op, d: u16, c: u64, ins: &[V], at: &str) -> Result { + // An integer converted to a float: signed or unsigned, at its width. + if d == 33 || d == 34 { + return match ins.first() { + Some(V::Int(w, IntV::Known(v))) => { + let v = *v & mask(*w); + let s = if *w == 0 || *w >= 64 { v as i64 } else { ((v << (64 - w)) as i64) >> (64 - w) }; + Ok(V::Float(if d == 33 { s as f64 } else { v as f64 })) + } + Some(V::Int(_, IntV::Cond(_))) => Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a float computed from a measured bit"))), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an integer operand is expected"))), + }; + } + let w = match o.outputs.first().and_then(|&v| fr.values.get(v as usize)) { + Some(Ty::Int(w)) => *w, + _ => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an integer operation whose output is not an integer"))), + }; + if (0..=4).contains(&d) { + let cw = [1u8, 8, 16, 32, 64][d as usize]; + if cw != w { + return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a {cw}-bit constant in a {w}-bit value"))); + } + return Ok(V::Int(w, IntV::Known(c & mask(w)))); + } + let arg = |k: usize| -> Result<&IntV, Diagnostic> { + match ins.get(k) { + Some(V::Int(_, v)) => Ok(v), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an integer operand is expected"))), + } + }; + let in_w = match ins.first() { + Some(V::Int(w, _)) => *w, + _ => w, + }; + // A bit from a measurement, in the logic a condition can hold. + let as_cond = |v: &IntV| match v { + IntV::Cond(c) => Some((**c).clone()), + IntV::Known(k) => Some(Cond::Const(k & 1 == 1)), + }; + let any_cond = ins.iter().any(|x| matches!(x, V::Int(_, IntV::Cond(_)))); + if any_cond { + let c = match d { + 14 if in_w == 1 => Cond::not(as_cond(arg(0)?).expect("a bit")), + 11 | 12 | 13 | 19 if in_w == 1 => { + let (a, b) = (as_cond(arg(0)?).expect("a bit"), as_cond(arg(1)?).expect("a bit")); + match d { + 11 => Cond::and(a, b), + 12 => Cond::or(a, b), + 13 => Cond::xor(a, b), + _ => Cond::not(Cond::xor(a, b)), + } + } + 29 if w == 1 => { + let (s, a, b) = (as_cond(arg(0)?).expect("a bit"), as_cond(arg(1)?).expect("a bit"), as_cond(arg(2)?).expect("a bit")); + Cond::or(Cond::and(s.clone(), a), Cond::and(Cond::not(s), b)) + } + // A bit widened is the same bit. + 30 | 31 if in_w == 1 && w >= 1 => return Ok(V::Int(w, arg(0)?.clone())), + _ => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: integer arithmetic on a measured bit: the reader holds a measured bit only as a condition (not, and, or, xor, eq, select)"))), + }; + return Ok(V::Int(w, self.cond(c, at)?)); + } + let k = |i: usize| -> Result { + match arg(i)? { + IntV::Known(v) => Ok(*v), + IntV::Cond(_) => unreachable!(), + } + }; + let sx = |v: u64, w: u8| -> i64 { if w >= 64 || w == 0 { v as i64 } else { ((v << (64 - w)) as i64) >> (64 - w) } }; + let bin = |_: &Self| -> Result<(u64, u64), Diagnostic> { Ok((k(0)?, k(1)?)) }; + let v: u64 = match d { + 5 => { + let (a, b) = bin(self)?; + a.wrapping_add(b) + } + 6 => { + let (a, b) = bin(self)?; + a.wrapping_sub(b) + } + 7 => { + let (a, b) = bin(self)?; + a.wrapping_mul(b) + } + 8 | 9 | 25 | 26 => { + let (a, b) = bin(self)?; + if b & mask(in_w) == 0 { + return Err(Diagnostic::new(diag::DIVISION_BY_ZERO, None, format!("{at}: division by zero"))); + } + match d { + 8 => sx(a, in_w).wrapping_div(sx(b, in_w)) as u64, + 9 => (a & mask(in_w)) / (b & mask(in_w)), + 25 => sx(a, in_w).wrapping_rem(sx(b, in_w)) as u64, + _ => (a & mask(in_w)) % (b & mask(in_w)), + } + } + 10 => { + let (a, b) = bin(self)?; + a.wrapping_pow(b.min(u64::from(u32::MAX)) as u32) + } + 11 => k(0)? & k(1)?, + 12 => k(0)? | k(1)?, + 13 => k(0)? ^ k(1)?, + 14 => !k(0)?, + 15 => sx(k(0)?, in_w).min(sx(k(1)?, in_w)) as u64, + 16 => (k(0)? & mask(in_w)).min(k(1)? & mask(in_w)), + 17 => sx(k(0)?, in_w).max(sx(k(1)?, in_w)) as u64, + 18 => (k(0)? & mask(in_w)).max(k(1)? & mask(in_w)), + 19 => u64::from(k(0)? & mask(in_w) == k(1)? & mask(in_w)), + 20 => u64::from(sx(k(0)?, in_w) < sx(k(1)?, in_w)), + 21 => u64::from(sx(k(0)?, in_w) <= sx(k(1)?, in_w)), + 22 => u64::from(k(0)? & mask(in_w) < k(1)? & mask(in_w)), + 23 => u64::from(k(0)? & mask(in_w) <= k(1)? & mask(in_w)), + 24 => sx(k(0)?, in_w).unsigned_abs(), + 27 => u32::try_from(k(1)?).ok().and_then(|n| k(0).ok()?.checked_shl(n)).unwrap_or(0), + 28 => u32::try_from(k(1)?).ok().and_then(|n| (k(0).ok()? & mask(in_w)).checked_shr(n)).unwrap_or(0), + 29 => { + if k(0)? & 1 == 1 { + k(1)? + } else { + k(2)? + } + } + 30 => sx(k(0)?, in_w) as u64, + 31 | 32 => k(0)? & mask(in_w), + k => return Err(unread(format!("{at}: an integer operation of kind {k}, which the schema does not have"))), + }; + Ok(V::Int(w, IntV::Known(v & mask(w)))) + } + + #[allow(clippy::too_many_arguments)] + fn int_array_op(&mut self, fr: &Frame, o: &Op, d: u16, consts: &[u64], zw: u8, ins: Vec, at: &str) -> Result { + let w = match o.outputs.first().and_then(|&v| fr.values.get(v as usize)) { + Some(Ty::IntArray(w, _)) => *w, + Some(Ty::Int(w)) => *w, + _ => 64, + }; + Ok(match d { + 0..=4 => { + self.array_len(consts.len(), at)?; + V::IntArray(w, consts.iter().map(|&c| IntV::Known(c & mask(w))).collect()) + } + 5 => { + let n = self.known_int(ins.first().unwrap_or(&V::Used), at, "an array's length")?; + let n = self.array_len(usize::try_from(n).unwrap_or(usize::MAX), at)?; + V::IntArray(zw, vec![IntV::Known(0); n]) + } + 6 => { + let (V::IntArray(aw, a), Some(i)) = (&ins[0], ins.get(1)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: getIndex takes an array and an index"))) }; + let i = usize::try_from(self.known_int(i, at, "an index")?).unwrap_or(usize::MAX); + let x = a.get(i).cloned().ok_or_else(|| Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {}", a.len())))?; + V::Int(*aw, x) + } + 7 => { + let (V::IntArray(aw, a), Some(i), Some(V::Int(_, x))) = (&ins[0], ins.get(1), ins.get(2)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: setIndex takes an array, an index and a value"))) }; + let i = usize::try_from(self.known_int(i, at, "an index")?).unwrap_or(usize::MAX); + let n = a.len(); + if i >= n { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {n}"))); + } + // The array is copied (the old value may still be read), and the copy counts. + self.array_len(n, at)?; + let mut a = a.clone(); + a[i] = x.clone(); + V::IntArray(*aw, a) + } + 8 => match &ins[0] { + V::IntArray(_, a) => V::Int(32, IntV::Known(a.len() as u64)), + _ => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: length of something that is not an array"))), + }, + 9 => V::IntArray( + w, + self.array_len(ins.len(), at).map(|_| ins.iter())? + .map(|x| match x { + V::Int(_, v) => Ok(v.clone()), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: an array of integers is made of integers"))), + }) + .collect::>()?, + ), + k => return Err(unread(format!("{at}: an integer-array operation of kind {k}, which the schema does not have"))), + }) + } + + fn float_op(&mut self, d: u16, c: f64, ins: &[V], int_out: Option, at: &str) -> Result { + use crate::qasm::value as m; + let f = |k: usize| -> Result { + match ins.get(k) { + Some(V::Float(x)) => Ok(*x), + _ => Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a float operand is expected"))), + } + }; + let b = |x: bool| V::Int(1, IntV::Known(u64::from(x))); + Ok(match d { + 0 | 1 => V::Float(c), + 2 => V::Float(f(0)? + f(1)?), + 3 => V::Float(f(0)? - f(1)?), + 4 => V::Float(f(0)? * f(1)?), + 32 => V::Float(f(0)? / f(1)?), + 5 => V::Float(m::powf(f(0)?, f(1)?)), + 6 => b(f(0)? == f(1)?), + 7 => b(f(0)? < f(1)?), + 8 => b(f(0)? <= f(1)?), + 9 => V::Float(f(0)?.sqrt()), + 10 => V::Float(f(0)?.abs()), + 11 => V::Float(f(0)?.ceil()), + 12 => V::Float(f(0)?.floor()), + 13 => b(f(0)?.is_nan()), + 14 => b(f(0)?.is_infinite()), + 15 => V::Float(m::exp(f(0)?)), + 16 => V::Float(m::ln(f(0)?)), + 17 => V::Float(m::sin(f(0)?)), + 18 => V::Float(m::cos(f(0)?)), + 19 => V::Float(m::tan(f(0)?)), + 20 => V::Float(m::arcsin(f(0)?)), + 21 => V::Float(m::arccos(f(0)?)), + 22 => V::Float(m::arctan(f(0)?)), + 23 => V::Float(crate::repro::atan2(f(0)?, f(1)?)), + 24 => { + let x = f(0)?; + V::Float((m::exp(x) - m::exp(-x)) / 2.0) + } + 25 => { + let x = f(0)?; + V::Float((m::exp(x) + m::exp(-x)) / 2.0) + } + 26 => { + let x = f(0)?; + let (a, z) = (m::exp(x), m::exp(-x)); + V::Float((a - z) / (a + z)) + } + 27 => { + let x = f(0)?; + V::Float(m::ln(x + (x * x + 1.0).sqrt())) + } + 28 => { + let x = f(0)?; + V::Float(m::ln(x + (x * x - 1.0).sqrt())) + } + 29 => { + let x = f(0)?; + V::Float(0.5 * m::ln((1.0 + x) / (1.0 - x))) + } + 30 => V::Float(f(0)?.max(f(1)?)), + 31 => V::Float(f(0)?.min(f(1)?)), + 33 => match ins.first() { + Some(V::Int(_, IntV::Known(s))) => V::Float(if s & 1 == 1 { f(1)? } else { f(2)? }), + _ => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: a float chosen by a measured bit"))), + }, + 34 => V::Float(f(0)?), + 35 => V::Float(f64::from(f(0)? as f32)), + // A float converted to an integer of the output's width, toward zero. + 36 | 37 => { + let w = int_out.ok_or_else(|| Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: a float converted to an integer has an integer output")))?; + let x = f(0)?; + if !x.is_finite() { + return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: {x} converted to an integer"))); + } + let v = if d == 36 { x as i64 as u64 } else { x as u64 }; + V::Int(w, IntV::Known(v & mask(w))) + } + k => return Err(unread(format!("{at}: a float operation of kind {k}, which the schema does not have"))), + }) + } + + fn float_array_op(&mut self, d: u16, consts: &[f64], ins: Vec, at: &str) -> Result { + Ok(match d { + 0 | 1 => { + self.array_len(consts.len(), at)?; + V::FloatArray(consts.to_vec()) + } + 2 => { + let n = self.known_int(ins.first().unwrap_or(&V::Used), at, "an array's length")?; + let n = self.array_len(usize::try_from(n).unwrap_or(usize::MAX), at)?; + V::FloatArray(vec![0.0; n]) + } + 3 => { + let (V::FloatArray(a), Some(i)) = (&ins[0], ins.get(1)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: getIndex takes an array and an index"))) }; + let i = usize::try_from(self.known_int(i, at, "an index")?).unwrap_or(usize::MAX); + V::Float(*a.get(i).ok_or_else(|| Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {}", a.len())))?) + } + 4 => { + let (V::FloatArray(a), Some(i), Some(V::Float(x))) = (&ins[0], ins.get(1), ins.get(2)) else { return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: setIndex takes an array, an index and a float"))) }; + let i = usize::try_from(self.known_int(i, at, "an index")?).unwrap_or(usize::MAX); + let n = a.len(); + if i >= n { + return Err(Diagnostic::new(diag::OUT_OF_RANGE_INDEX, None, format!("{at}: index {i} of an array of {n}"))); + } + self.array_len(n, at)?; + let mut a = a.clone(); + a[i] = *x; + V::FloatArray(a) + } + 5 => match &ins[0] { + V::FloatArray(a) => V::Int(32, IntV::Known(a.len() as u64)), + _ => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: length of something that is not an array"))), + }, + 6 => { + self.array_len(ins.len(), at)?; + V::FloatArray(ins.iter().map(|x| self.float(x, at)).collect::>()?) + } + k => return Err(unread(format!("{at}: a float-array operation of kind {k}, which the schema does not have"))), + }) + } + + /// What a value is after a branch on `c`: the same on both sides, or (a + /// bit, or an array of them) one side's bit or the other's, as a condition. + fn join(&mut self, c: &Cond, a: V, b: V, at: &str) -> Result { + if a == b { + return Ok(a); + } + match (a, b) { + (V::Int(1, x), V::Int(1, y)) => { + let as_c = |v: IntV| match v { + IntV::Cond(c) => Rc::try_unwrap(c).unwrap_or_else(|rc| (*rc).clone()), + IntV::Known(k) => Cond::Const(k & 1 == 1), + }; + let merged = Cond::or(Cond::and(c.clone(), as_c(x)), Cond::and(Cond::not(c.clone()), as_c(y))); + Ok(V::Int(1, self.cond(merged, at)?)) + } + (V::Qubit(_), V::Qubit(_)) | (V::Qureg(_), V::Qureg(_)) => Err(Diagnostic::new(diag::STALE_READ, None, format!("{at}: a qubit leaves the two sides of a branch in different places"))), + (V::IntArray(w, x), V::IntArray(_, y)) if w == 1 && x.len() == y.len() => { + self.array_len(x.len(), at)?; + let mut out = Vec::with_capacity(x.len()); + for (p, q) in x.into_iter().zip(y) { + match self.join(c, V::Int(1, p), V::Int(1, q), at)? { + V::Int(_, v) => out.push(v), + _ => unreachable!("two bits join to a bit"), + } + } + Ok(V::IntArray(1, out)) + } + _ => Ok(V::Unknown("it differs between the two sides of a branch on a measurement".into())), + } + } +} + +fn mask(w: u8) -> u64 { + if w >= 64 { u64::MAX } else { (1u64 << w) - 1 } +} + +/// An order to run a region's operations in: the order they are listed in +/// where that defines every value before its use, else an order their values +/// allow (the first ready operation each time). +fn order(r: &Region, n_values: usize) -> Result, String> { + let mut producer: BTreeMap = BTreeMap::new(); + for (k, o) in r.ops.iter().enumerate() { + for &v in &o.outputs { + if (v as usize) >= n_values { + return Err(format!("operation {k} gives value {v}, outside the function's {n_values} values")); + } + if producer.insert(v, k).is_some() { + return Err(format!("value {v} is given by two operations")); + } + } + } + let deps: Vec> = r.ops.iter().map(|o| inputs_of(o).iter().filter_map(|v| producer.get(v).copied()).collect()).collect(); + let mut indeg: Vec = deps.iter().map(Vec::len).collect(); + let mut users: Vec> = vec![Vec::new(); r.ops.len()]; + for (k, ds) in deps.iter().enumerate() { + for &d in ds { + users[d].push(k); + } + } + let mut ready: std::collections::BinaryHeap> = (0..r.ops.len()).filter(|&k| indeg[k] == 0).map(std::cmp::Reverse).collect(); + let mut out = Vec::with_capacity(r.ops.len()); + while let Some(std::cmp::Reverse(k)) = ready.pop() { + out.push(k); + for &u in &users[k] { + indeg[u] -= 1; + if indeg[u] == 0 { + ready.push(std::cmp::Reverse(u)); + } + } + } + if out.len() != r.ops.len() { + return Err("its operations' values form a cycle".into()); + } + Ok(out) +} + +/// The values an operation reads: its inputs (a region inside it reads its +/// own sources, which its operation's inputs give). +fn inputs_of(o: &Op) -> Vec { + o.inputs.clone() +} + +/// Reads a jeff module. +pub fn read(bytes: &[u8]) -> Result { + if bytes.len() > MAX_BYTES { + return Err(unread(format!("a module of {} bytes, larger than the {MAX_BYTES} the reader takes", bytes.len()))); + } + let m = decode(bytes).map_err(|e| unread(format!("not a jeff module: {e}")))?; + let (maj, min, patch) = m.version; + if maj != 0 || !(2..=3).contains(&min) { + return Err(Diagnostic::new(diag::FORMAT_VERSION, None, format!("jeff {maj}.{min}.{patch}: the reader takes 0.2 and 0.3"))); + } + let entry = m.entry; + let (body, values) = match m.functions.get(entry as usize) { + Some((_, Func::Definition { body, values })) => (body, values), + Some((_, Func::Declaration)) => return Err(Diagnostic::new(diag::INSTRUCTION_UNKNOWN, None, "the entry point is declared and not defined")), + None => return Err(unread(format!("the entry point is function {entry}, which the module does not have"))), + }; + let mut r = Runner { m: &m, n_qubits: 0, n_bits: 0, steps: 0, notes: BTreeMap::new(), custom: BTreeMap::new(), ppr: 0, arbitrary: 0, emitted: 0, units: 0 }; + r.charge(values.len(), "the entry point's values")?; + let mut fr = Frame { f: entry, values, vals: vec![None; values.len()] }; + // The entry point's arguments: qubits, from |0⟩. + let mut args = Vec::new(); + for &s in &body.sources { + let at = format!("in function `{}`, the entry point's arguments", r.name(entry)); + match values.get(s as usize) { + Some(Ty::Qubit) => args.push(V::Qubit(r.fresh_qubit(&at)?)), + Some(Ty::Qureg(Some(n))) => { + let q = r.fresh_register(*n as usize, &at)?; + args.push(V::Qureg(q)); + } + Some(t) => return Err(Diagnostic::new(diag::JEFF_NOT_KNOWN, None, format!("{at}: an entry point that takes {t:?}, which is not known on reading"))), + None => return Err(Diagnostic::new(diag::JEFF_LINEAR, None, format!("{at}: value {s} is outside the function's values"))), + } + } + if !args.is_empty() { + r.note(diag::JEFF_ENTRY_QUBITS, format!("the entry point takes {} qubit argument{}: they are the program's first qubits, from |0⟩, and what it returns is not measured", args.len(), if args.len() == 1 { "" } else { "s" })); + } + let mut out = Vec::new(); + let targets = r.region(&mut fr, body, args, &mut out, 0)?; + if r.n_qubits == 0 { + return Err(Diagnostic::new(diag::NO_QUBITS, None, format!("the entry point `{}` uses no qubits", r.name(entry)))); + } + // The bits it returns, in order, are the program's. + let mut returned: Vec> = Vec::new(); + let mut plain = true; + for t in &targets { + let entries: Vec = match t { + V::Int(1, v) => vec![v.clone()], + V::IntArray(1, a) => a.clone(), + _ => continue, + }; + for e in entries { + match e { + IntV::Known(0) => returned.push(None), + IntV::Cond(c) => match *c { + Cond::Bit(b) if !returned.contains(&Some(b)) => returned.push(Some(b)), + _ => plain = false, + }, + _ => plain = false, + } + } + } + let mut n_bits = r.n_bits; + let mut remap: Vec = (0..r.n_bits).collect(); + let mut bits_note = "the bits are in the order the program measures".to_string(); + if plain && !returned.is_empty() { + let mut next = returned.len() as u32; + let mut placed = vec![false; r.n_bits as usize]; + for (i, e) in returned.iter().enumerate() { + if let Some(b) = e { + remap[*b as usize] = i as u32; + placed[*b as usize] = true; + } + } + for b in 0..r.n_bits as usize { + if !placed[b] { + remap[b] = next; + next += 1; + } + } + n_bits = next; + bits_note = "the bits are those the entry point returns, in its order".into(); + renumber(&mut out, &remap); + } + let mut notes: Vec = std::mem::take(&mut r.notes).into_iter().map(|(code, (_, what))| Diagnostic::new(code, None, what)).collect(); + notes.extend(collect_notes(&r)); + let circuit = Circuit { n_qubits: r.n_qubits, n_bits, body: out }; + let tool = if m.tool.is_empty() { String::new() } else { format!(", written by {} {}", m.tool, m.tool_version).trim_end().to_string() }; + let qasm = circuit.to_qasm(&format!("Read from jeff {maj}.{min}.{patch}{tool}, entry point `{}`: {bits_note}.", r.name(entry))); + let summary = json!({ + "format": "jeff", + "version": format!("{maj}.{min}.{patch}"), + "tool": m.tool, + "tool_version": m.tool_version, + "entry_point": r.name(entry), + "functions": m.functions.len(), + "qubits": r.n_qubits, + "bits": n_bits, + "bits_returned": plain && !returned.is_empty(), + "pauli_product_rotations": r.ppr, + "custom_gates": r.custom, + "arbitrary_angles": r.arbitrary, + }); + Ok(Reading { qasm, notes, summary }) +} + +fn collect_notes(r: &Runner) -> Vec { + let mut v = Vec::new(); + for (name, n) in &r.custom { + v.push(Diagnostic::new(diag::JEFF_CUSTOM_NAMED, None, format!("the custom gate `{name}` ({n} use{}) read as the standard library's `{}`, by its name", if *n == 1 { "" } else { "s" }, name.to_ascii_lowercase()))); + } + if r.ppr > 0 { + v.push(Diagnostic::new(diag::JEFF_PPR, None, format!("{} Pauli-product rotation{} exp(iθP) lowered exactly to basis changes, a parity ladder and one rz", r.ppr, if r.ppr == 1 { "" } else { "s" }))); + } + if r.arbitrary > 0 { + v.push(Diagnostic::new(diag::ARBITRARY_ANGLE, None, format!("{} rotation{} at angles that are not dyadic multiples of 2π: the double-precision methods take them; the bit-exact method refuses them (Q500)", r.arbitrary, if r.arbitrary == 1 { "" } else { "s" }))); + } + v +} + +fn renumber(body: &mut [Stmt], map: &[u32]) { + fn cond(c: &mut Cond, map: &[u32]) { + match c { + Cond::Const(_) => {} + Cond::Bit(b) => *b = map[*b as usize], + Cond::Not(x) => cond(x, map), + Cond::And(a, b) | Cond::Or(a, b) => { + cond(a, map); + cond(b, map); + } + } + } + for s in body { + match s { + Stmt::Measure { bit, .. } => *bit = map[*bit as usize], + Stmt::If { cond: c, then, other } => { + cond(c, map); + renumber(then, map); + renumber(other, map); + } + _ => {} + } + } +} + +// --------------------------------------------------------------------------- +// Writing +// --------------------------------------------------------------------------- + +struct W { + values: Vec, + ops: Vec, + cur: Vec, +} + +fn ty_out(t: &Ty) -> Out { + let mut o = Out::new(2, 0); + match t { + Ty::Qubit => o.set_u16(0, 0), + Ty::Qureg(n) => { + o.set_u16(0, 1); + if let Some(n) = n { + o.set_u16(1, 1); + o.set_u32(1, *n); + } + } + Ty::Int(w) => { + o.set_u16(0, 2); + o.set_u8(2, *w); + } + Ty::IntArray(w, n) => { + o.set_u16(0, 3); + o.set_u8(2, *w); + if let Some(n) = n { + o.set_u16(2, 1); + o.set_u32(2, *n); + } + } + Ty::Float(p) => { + o.set_u16(0, 4); + o.set_u16(1, *p); + } + Ty::FloatArray(p, n) => { + o.set_u16(0, 5); + o.set_u16(1, *p); + if let Some(n) = n { + o.set_u16(2, 1); + o.set_u32(2, *n); + } + } + } + o +} + +impl W { + fn value(&mut self, t: Ty) -> u32 { + self.values.push(t); + self.values.len() as u32 - 1 + } + /// An operation: `kind` is the instruction's union member, `body` its struct. + fn op(&mut self, inputs: Vec, outputs: Vec, kind: u16, body: Out) { + let mut o = Out::new(1, 4); + o.set_u16(0, kind); + o.ptrs[0] = Ptr::U32s(inputs); + o.ptrs[1] = Ptr::U32s(outputs); + o.ptrs[3] = Ptr::Struct(Box::new(body)); + self.ops.push(o); + } + fn float(&mut self, x: f64) -> u32 { + let v = self.value(Ty::Float(1)); + let mut b = Out::new(2, 0); + b.set_u16(2, 1); + b.set_f64(1, x); + self.op(vec![], vec![v], 4, b); + v + } + fn int32(&mut self, x: u32) -> u32 { + let v = self.value(Ty::Int(32)); + let mut b = Out::new(2, 0); + b.set_u16(1, 3); + b.set_u32(1, x); + self.op(vec![], vec![v], 2, b); + v + } + fn qubit_op(&mut self, kind: u16, gate: Option, inputs: Vec, outputs: Vec) { + let mut q = Out::new(1, 1); + q.set_u16(0, kind); + if let Some(g) = gate { + q.ptrs[0] = Ptr::Struct(Box::new(g)); + } + self.op(inputs, outputs, 0, q); + } + /// A gate on `targets` under `controls`: a well-known gate, or a + /// Pauli-product rotation when `ppr` is given. + fn gate(&mut self, well_known: u16, ppr: Option>, targets: &[u32], controls: &[u32], params: &[f64], adjoint: bool) { + let mut g = Out::new(2, 1); + match ppr { + Some(p) => { + g.set_u16(1, 2); + g.ptrs[0] = Ptr::U16s(p); + } + None => g.set_u16(0, well_known), + } + g.set_u8(6, controls.len() as u8); + g.set_bool(56, adjoint); + let floats: Vec = params.iter().map(|&p| self.float(p)).collect(); + let qs: Vec = targets.iter().chain(controls).copied().collect(); + let mut inputs: Vec = qs.iter().map(|&q| self.cur[q as usize]).collect(); + inputs.extend(floats); + let outputs: Vec = qs.iter().map(|_| self.value(Ty::Qubit)).collect(); + for (&q, &o) in qs.iter().zip(&outputs) { + self.cur[q as usize] = o; + } + self.qubit_op(6, Some(g), inputs, outputs); + } +} + +// The well-known gates' enum values. +const X: u16 = 0; +const Y: u16 = 1; +const Z: u16 = 2; +const S: u16 = 3; +const T: u16 = 4; +const R1: u16 = 5; +const RX: u16 = 6; +const RY: u16 = 7; +const RZ: u16 = 8; +const H: u16 = 9; +const U: u16 = 10; +const SWAP: u16 = 11; +const I: u16 = 12; +const GPHASE: u16 = 13; + +fn write_op(w: &mut W, kind: Kind, q: &[u32], p: &[f64]) { + use std::f64::consts::{FRAC_PI_2, FRAC_PI_4}; + let one = |w: &mut W, g: u16, a: u32, params: &[f64], adj: bool| w.gate(g, None, &[a], &[], params, adj); + match kind { + Kind::Id => one(w, I, q[0], &[], false), + Kind::X => one(w, X, q[0], &[], false), + Kind::Y => one(w, Y, q[0], &[], false), + Kind::Z => one(w, Z, q[0], &[], false), + Kind::H => one(w, H, q[0], &[], false), + Kind::S => one(w, S, q[0], &[], false), + Kind::Sdg => one(w, S, q[0], &[], true), + Kind::T => one(w, T, q[0], &[], false), + Kind::Tdg => one(w, T, q[0], &[], true), + // √X = e^{iπ/4} Rx(π/2). + Kind::Sx => { + one(w, RX, q[0], &[FRAC_PI_2], false); + w.gate(GPHASE, None, &[], &[], &[FRAC_PI_4], false); + } + Kind::Sxdg => { + one(w, RX, q[0], &[-FRAC_PI_2], false); + w.gate(GPHASE, None, &[], &[], &[-FRAC_PI_4], false); + } + Kind::Rx => one(w, RX, q[0], p, false), + Kind::Ry => one(w, RY, q[0], p, false), + Kind::Rz => one(w, RZ, q[0], p, false), + Kind::P => one(w, R1, q[0], p, false), + Kind::U => one(w, U, q[0], p, false), + Kind::Cx => w.gate(X, None, &[q[1]], &[q[0]], &[], false), + Kind::Cy => w.gate(Y, None, &[q[1]], &[q[0]], &[], false), + Kind::Cz => w.gate(Z, None, &[q[1]], &[q[0]], &[], false), + Kind::Ch => w.gate(H, None, &[q[1]], &[q[0]], &[], false), + Kind::Cp => w.gate(R1, None, &[q[1]], &[q[0]], p, false), + Kind::Crx => w.gate(RX, None, &[q[1]], &[q[0]], p, false), + Kind::Cry => w.gate(RY, None, &[q[1]], &[q[0]], p, false), + Kind::Crz => w.gate(RZ, None, &[q[1]], &[q[0]], p, false), + // Controlled e^{iγ} U(θ, φ, λ): the controlled U, then the phase γ on the control. + Kind::Cu => { + w.gate(U, None, &[q[1]], &[q[0]], &p[..3], false); + one(w, R1, q[0], &[p[3]], false); + } + Kind::Swap => w.gate(SWAP, None, &[q[0], q[1]], &[], &[], false), + Kind::Iswap => { + one(w, S, q[0], &[], false); + one(w, S, q[1], &[], false); + one(w, H, q[0], &[], false); + w.gate(X, None, &[q[1]], &[q[0]], &[], false); + w.gate(X, None, &[q[0]], &[q[1]], &[], false); + one(w, H, q[1], &[], false); + } + // Rpp(θ) = exp(−iθ/2 P⊗P), and the schema's rotation is exp(iφP). + Kind::Rxx => w.gate(0, Some(vec![1, 1]), &[q[0], q[1]], &[], &[-p[0] / 2.0], false), + Kind::Ryy => w.gate(0, Some(vec![2, 2]), &[q[0], q[1]], &[], &[-p[0] / 2.0], false), + Kind::Rzz => w.gate(0, Some(vec![3, 3]), &[q[0], q[1]], &[], &[-p[0] / 2.0], false), + Kind::Ccx => w.gate(X, None, &[q[2]], &[q[0], q[1]], &[], false), + Kind::Ccz => w.gate(Z, None, &[q[2]], &[q[0], q[1]], &[], false), + Kind::Cswap => w.gate(SWAP, None, &[q[1], q[2]], &[q[0]], &[], false), + } +} + +/// Writes `p` as a jeff module. +pub fn write(p: &Program) -> Result<(Vec, Vec), Diagnostic> { + let mut notes = Vec::new(); + super::qir::dropped(p, &mut notes); + let mut w = W { values: Vec::new(), ops: Vec::new(), cur: Vec::new() }; + for _ in 0..p.n_qubits { + let v = w.value(Ty::Qubit); + w.qubit_op(0, None, vec![], vec![v]); + w.cur.push(v); + } + let static_steps: Vec; + let steps: &[Instr] = match &p.dynamic { + Some(s) => s, + None => { + static_steps = (0..p.ops.len()).map(Instr::Gate).chain(p.measured.iter().map(|&(qubit, bit)| Instr::Measure { qubit, bit })).collect(); + &static_steps + } + }; + let mut n_bits = p.n_bits; + for s in steps { + match s { + Instr::Measure { bit, .. } => n_bits = n_bits.max(bit + 1), + Instr::Jump(_) | Instr::JumpUnless(..) => { + return Err(Diagnostic::new(diag::WRITE_CANNOT, None, "this program branches on what it measured: the jeff writer writes programs whose steps are gates, measurements and resets, without conditions")); + } + _ => {} + } + } + // The bits, as an array the program returns. + let mut bits = None; + if n_bits > 0 { + let v = w.value(Ty::IntArray(1, Some(n_bits))); + let mut a = Out::new(1, 1); + a.set_u16(0, 0); + a.ptrs[0] = Ptr::Bools(vec![false; n_bits as usize]); + w.op(vec![], vec![v], 3, a); + bits = Some(v); + } + // A qubit measured for the last time, with nothing after it, is measured destructively. + let mut last_use = vec![usize::MAX; p.n_qubits as usize]; + for (i, s) in steps.iter().enumerate() { + let qs: Vec = match s { + Instr::Gate(k) => p.ops[*k].qubits.clone(), + Instr::Measure { qubit, .. } | Instr::Reset(qubit) => vec![*qubit], + _ => vec![], + }; + for q in qs { + last_use[q as usize] = i; + } + } + let mut consumed = vec![false; p.n_qubits as usize]; + for (i, s) in steps.iter().enumerate() { + match s { + Instr::Gate(k) => { + let op = &p.ops[*k]; + write_op(&mut w, op.kind, &op.qubits, &op.params); + } + Instr::Measure { qubit, bit } => { + let q = *qubit as usize; + let r = w.value(Ty::Int(1)); + if last_use[q] == i { + w.qubit_op(3, None, vec![w.cur[q]], vec![r]); + consumed[q] = true; + } else { + let nq = w.value(Ty::Qubit); + w.qubit_op(4, None, vec![w.cur[q]], vec![nq, r]); + w.cur[q] = nq; + } + let idx = w.int32(*bit); + let arr = bits.expect("bits"); + let next = w.value(Ty::IntArray(1, Some(n_bits))); + let mut set = Out::new(1, 1); + set.set_u16(0, 7); + w.op(vec![arr, idx, r], vec![next], 3, set); + bits = Some(next); + } + Instr::Reset(q) => { + let nq = w.value(Ty::Qubit); + w.qubit_op(5, None, vec![w.cur[*q as usize]], vec![nq]); + w.cur[*q as usize] = nq; + } + _ => unreachable!("refused above"), + } + } + let cur = w.cur.clone(); + for (q, &done) in consumed.iter().enumerate() { + if !done { + w.qubit_op(1, None, vec![cur[q]], vec![]); + } + } + let mut region = Out::new(0, 4); + region.ptrs[0] = Ptr::U32s(vec![]); + region.ptrs[1] = Ptr::U32s(bits.into_iter().collect()); + region.ptrs[2] = Ptr::Structs { data_words: 1, ptr_count: 4, items: std::mem::take(&mut w.ops) }; + let mut func = Out::new(1, 3); + func.set_u16(0, 0); + func.set_u16(1, 0); + func.ptrs[0] = Ptr::Struct(Box::new(region)); + func.ptrs[1] = Ptr::Structs { + data_words: 0, + ptr_count: 2, + items: w + .values + .iter() + .map(|t| { + let mut v = Out::new(0, 2); + v.ptrs[0] = Ptr::Struct(Box::new(ty_out(t))); + v + }) + .collect(), + }; + let mut module = Out::new(2, 5); + module.set_u32(0, VERSION.0); + module.set_u32(2, VERSION.1); + module.set_u32(3, VERSION.2); + module.set_u16(2, 0); + module.ptrs[0] = Ptr::Structs { data_words: 1, ptr_count: 3, items: vec![func] }; + module.ptrs[1] = Ptr::Texts(vec!["main".into()]); + module.ptrs[3] = Ptr::Text("wai-quantum".into()); + module.ptrs[4] = Ptr::Text(env!("CARGO_PKG_VERSION").into()); + Ok((super::capnp::write(&module), notes)) +} diff --git a/wai/wai-quantum/src/engine/formats/mod.rs b/wai/wai-quantum/src/engine/formats/mod.rs new file mode 100644 index 000000000..9d489c195 --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/mod.rs @@ -0,0 +1,538 @@ +//! Programs in other formats, read to the engine's one program and written +//! from it: QIR (LLVM text, under its base and adaptive profiles, [`qir`]) +//! and jeff (the structured exchange format, a Cap'n Proto message, +//! [`jeff`]). +//! +//! A reader gives the program as OpenQASM 3, which the one reader every +//! surface uses ([`crate::qasm`]) then reads, so a program from any format +//! plans, runs, checks and draws as one written in OpenQASM. Every +//! construct a reader cannot hold exactly is refused with a code of the +//! reader's table (`Q700`–`Q721`, [`crate::qasm::diag`]) and where it stands +//! (a line of a QIR module; a function and an operation of a jeff module); +//! what it reads with something to know (a rotation at an arbitrary angle, a +//! custom gate read by its name) is a note with its code. +//! +//! [`convert`] carries a program from one format to another and checks the +//! result: what it writes is read back and held to the program by the +//! equivalence ladder ([`super::equiv`]), and a program read from another +//! format is written back to it and read again, the two readings held to +//! each other the same way. [`convert_json`] is the same as JSON, the form +//! every surface serves (the command line's `convert`, both MCP servers' +//! `convert_program`, the service's `POST /v1/convert`, the Studio's +//! `engine_convert`); a binary format travels in it as base64. +//! +//! [`expand`] lets a job carry its program in another format: +//! `{"source": {"format": "qir", "program": …}}` in place of `"qasm"` +//! (`"program_base64"` for jeff) is read to the job's `"qasm"` before +//! anything else reads the job, and the request keeps its source. + +pub mod jeff; +pub mod qir; +mod capnp; +mod emit; + +pub use qir::Profile; + +use crate::qasm::diag::{self, Diagnostic}; +use crate::qasm::Program; +use serde_json::{Value, json}; + +/// The format of a conversion's answer. +pub const FORMAT: &str = "wai.quantum.convert/1"; + +/// A format a program is read from or written to. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum Format { + /// OpenQASM 3 (and the OpenQASM 2 forms in common use). + Qasm, + /// QIR, as LLVM text. + Qir, + /// jeff, as its Cap'n Proto message. + Jeff, +} + +impl Format { + /// Every format, in order. + pub fn all() -> &'static [Format] { + &[Format::Qasm, Format::Qir, Format::Jeff] + } + + pub fn name(self) -> &'static str { + match self { + Format::Qasm => "qasm", + Format::Qir => "qir", + Format::Jeff => "jeff", + } + } + + /// A format by name: `qasm` (`openqasm`, `openqasm3`), `qir` (`ll`) or `jeff`. + pub fn parse(name: &str) -> Option { + match name.trim().to_ascii_lowercase().as_str() { + "qasm" | "qasm3" | "qasm2" | "openqasm" | "openqasm3" | "openqasm2" => Some(Format::Qasm), + "qir" | "ll" => Some(Format::Qir), + "jeff" => Some(Format::Jeff), + _ => None, + } + } + + /// Whether the format is binary (and travels in JSON as base64). + pub fn is_binary(self) -> bool { + matches!(self, Format::Jeff) + } + + /// The format a file's extension names: `.qasm`, `.ll` or `.qir`, `.jeff`. + pub fn from_extension(path: &str) -> Option { + let ext = path.rsplit_once('.')?.1.to_ascii_lowercase(); + match ext.as_str() { + "qasm" | "inc" => Some(Format::Qasm), + "ll" | "qir" => Some(Format::Qir), + "jeff" => Some(Format::Jeff), + _ => None, + } + } + + /// The format a file's bytes look like: jeff when they are not text, QIR + /// when they are LLVM text calling quantum intrinsics, OpenQASM otherwise. + pub fn sniff(bytes: &[u8]) -> Format { + match std::str::from_utf8(bytes) { + Err(_) => Format::Jeff, + Ok(t) if t.contains("__quantum__") && (t.contains("define ") || t.contains("declare ")) => Format::Qir, + Ok(t) if t.contains('\0') => Format::Jeff, + Ok(_) => Format::Qasm, + } + } + + /// The file extension it is written with. + pub fn extension(self) -> &'static str { + match self { + Format::Qasm => "qasm", + Format::Qir => "ll", + Format::Jeff => "jeff", + } + } +} + +/// A program read from a format. +#[derive(Clone, Debug, PartialEq)] +pub struct Imported { + /// The program, as OpenQASM 3. + pub qasm: String, + /// The reader's warnings and notes. + pub notes: Vec, + /// What was read: the format, its version and what it declared. + pub summary: Value, +} + +/// A program written to a format. +#[derive(Clone, Debug, PartialEq)] +pub struct Exported { + pub bytes: Vec, + /// What was left out, with its code. + pub notes: Vec, + /// The format, its version and profile. + pub summary: Value, +} + +/// A program read from `input` in `format`, as OpenQASM 3 that the engine's +/// reader reads. +pub fn read(format: Format, input: &[u8]) -> Result { + let text = || std::str::from_utf8(input).map_err(|_| Diagnostic::new(diag::FORMAT_UNREAD, None, format!("{} is text, and this is not UTF-8", format.name()))); + let imported = match format { + Format::Qasm => { + let t = text()?; + crate::qasm::read(t)?; + return Ok(Imported { qasm: t.to_string(), notes: Vec::new(), summary: json!({ "format": "qasm" }) }); + } + Format::Qir => { + let r = qir::read(text()?)?; + Imported { qasm: r.qasm, notes: r.notes, summary: r.summary } + } + Format::Jeff => { + let r = jeff::read(input)?; + Imported { qasm: r.qasm, notes: r.notes, summary: r.summary } + } + }; + // The text a reader writes reads; if it did not, that is this reader's + // fault, and it is refused rather than passed on. + crate::qasm::read(&imported.qasm).map_err(|d| Diagnostic::new(d.code, None, format!("the program read from {} does not read as OpenQASM: {}", format.name(), d.message)))?; + Ok(imported) +} + +/// `p` written in `format`. `profile` is QIR's (the base profile when the +/// program's measurements all come at the end, else the adaptive one, when +/// none is given); other formats take none. +pub fn write(format: Format, p: &Program, profile: Option) -> Result { + match format { + Format::Qasm => { + if p.dynamic.is_some() { + return Err(Diagnostic::new(diag::WRITE_CANNOT, None, "a program that branches is written as OpenQASM from its own text, not from its steps")); + } + let text = super::rewrite::render(&p.ops, p.n_qubits, &p.measured, p.n_bits); + Ok(Exported { bytes: text.into_bytes(), notes: Vec::new(), summary: json!({ "format": "qasm" }) }) + } + Format::Qir => { + let (text, notes) = qir::write(p, profile)?; + let profile = if text.contains("\"qir_profiles\"=\"adaptive_profile\"") { "adaptive" } else { "base" }; + Ok(Exported { bytes: text.into_bytes(), notes, summary: json!({ "format": "qir", "version": "2.0", "profile": profile }) }) + } + Format::Jeff => { + if profile.is_some() { + return Err(Diagnostic::new(diag::WRITE_CANNOT, None, "a profile is QIR's; jeff takes none")); + } + let (bytes, notes) = jeff::write(p)?; + let (a, b, c) = jeff::VERSION; + Ok(Exported { bytes, notes, summary: json!({ "format": "jeff", "version": format!("{a}.{b}.{c}") }) }) + } + } +} + +/// A conversion: the program in the format asked for, the program as read, +/// what each side noted, and the check. +#[derive(Clone, Debug, PartialEq)] +pub struct Converted { + pub from: Format, + pub to: Format, + pub output: Vec, + /// The program as OpenQASM 3, as read from the input. + pub qasm: String, + pub notes: Vec, + pub read: Value, + pub written: Value, + /// How the output was held to the input (the equivalence ladder's + /// verdict, and on what), or null when no check was asked. + pub check: Value, +} + +/// `input` in `from` carried to `to`. With `check`, what is written is read +/// back and held to the program read by the equivalence ladder; a program +/// read into OpenQASM is written back to its own format and read again, and +/// the two readings held to each other. +pub fn convert(from: Format, to: Format, input: &[u8], profile: Option, check: bool) -> Result { + if profile.is_some() && to != Format::Qir { + return Err(Diagnostic::new(diag::WRITE_CANNOT, None, format!("a profile is QIR's; {} takes none", to.name()))); + } + let imported = read(from, input)?; + let program = crate::qasm::read(&imported.qasm)?.program; + let mut notes = imported.notes.clone(); + let (output, written) = if to == Format::Qasm { + (imported.qasm.clone().into_bytes(), json!({ "format": "qasm" })) + } else { + // (The same format as read is written afresh from the program read.) + let e = write(to, &program, profile)?; + notes.extend(e.notes); + (e.bytes, e.summary) + }; + let check = if !check { + Value::Null + } else if to != Format::Qasm { + let back = read(to, &output)?; + let p2 = crate::qasm::read(&back.qasm)?.program; + json!({ "what": format!("the {} written, read back, against the program read", to.name()), "verdict": held(&program, &imported.qasm, &p2, &back.qasm) }) + } else if from != Format::Qasm { + let again = write(from, &program, None).and_then(|e| read(from, &e.bytes)); + match again { + Ok(a) => { + let p2 = crate::qasm::read(&a.qasm)?.program; + json!({ "what": format!("the program read, written back as {} and read again, against the first reading", from.name()), "verdict": held(&program, &imported.qasm, &p2, &a.qasm) }) + } + Err(d) => json!({ "what": format!("the program read, written back as {}", from.name()), "verdict": { "verdict": "unchecked", "how": "none", "why": d.to_string() } }), + } + } else { + json!({ "what": "OpenQASM to OpenQASM: the text is the program", "verdict": { "verdict": "equivalent", "how": "identity", "why": "the program is passed through as it is" } }) + }; + Ok(Converted { from, to, output, qasm: imported.qasm, notes, read: imported.summary, written, check }) +} + +/// The ladder's verdict on two programs. Programs that measure in the middle +/// or branch are held to each other step for step, and where their steps +/// differ (a condition on several bits written as one branch per bit, say), +/// by the probability every outcome has: each branch at its measurements in +/// the middle with its weight, and each leaf's final measurements, from the +/// debugger. +fn held(a: &Program, a_text: &str, b: &Program, b_text: &str) -> Value { + if let Some(why) = too_much_to_check(a, b) { + return json!({ "verdict": "unchecked", "how": "none", "why": why }); + } + if a.dynamic.is_none() && b.dynamic.is_none() { + return super::equiv::equivalent_programs(a, b); + } + if a.n_qubits == b.n_qubits && a.ops == b.ops && a.dynamic == b.dynamic && a.measured == b.measured && a.n_bits == b.n_bits { + return json!({ "verdict": "equivalent", "how": "steps", "why": "the same gates, measurements, resets and branches, step for step" }); + } + if a.n_qubits != b.n_qubits || a.n_bits != b.n_bits { + return json!({ "verdict": "different", "how": "shape", "why": format!("one has {} qubits and {} bits, the other {} and {}", a.n_qubits, a.n_bits, b.n_qubits, b.n_bits) }); + } + match (distribution(a_text), distribution(b_text)) { + (Some(x), Some(y)) => { + let keys: std::collections::BTreeSet<&String> = x.keys().chain(y.keys()).collect(); + for k in keys { + let (p, q) = (x.get(k).copied().unwrap_or(0.0), y.get(k).copied().unwrap_or(0.0)); + if (p - q).abs() > 1e-9 { + return json!({ "verdict": "different", "how": "distribution", "why": format!("the outcome {k} has probability {p:.9} in one and {q:.9} in the other") }); + } + } + json!({ "verdict": "equivalent", "how": "distribution", "why": "every outcome has the same probability in both, from each branch at its measurements in the middle with its weight and each leaf's final measurements: the same results, which is evidence, not a proof that the circuits are the same" }) + } + _ => json!({ "verdict": "unchecked", "how": "none", "why": "the programs differ step by step, and their branches are more than the debugger enumerates here" }), + } +} + +/// The most a conversion's check spends, in gate applications times the +/// amplitudes (or tableau rows) each touches: past it the check is not run, +/// and the conversion says so. +pub const CHECK_WORK: f64 = 4e9; +/// The most gates a conversion's check applies, over every input it runs +/// (each basis state, each random input, each branch): each costs what +/// preparing it costs, whatever its width. +pub const CHECK_APPLICATIONS: f64 = 262_144.0; +/// The widest pair the check compares by their tableaus (whose memory grows +/// as the square of the qubits). +pub const CHECK_MAX_TABLEAU: u32 = 2048; + +/// Why the check of `a` against `b` is not run, when it would cost more than +/// [`CHECK_WORK`] or [`CHECK_APPLICATIONS`]: the tableau's rows per gate for +/// a Clifford pair; every basis state to 8 qubits, or three random inputs to +/// 22, through every gate; every branch of a program that branches (the +/// debugger's, at most 16 qubits and 1024 branches). +fn too_much_to_check(a: &Program, b: &Program) -> Option { + let n = a.n_qubits.max(b.n_qubits); + let gates = (a.ops.len() + b.ops.len()) as f64; + let amplitudes = f64::from(n.min(64)).exp2(); + let (applications, work) = if a.dynamic.is_some() || b.dynamic.is_some() { + let mids = |p: &Program| p.dynamic.as_ref().map_or(0, |s| s.iter().filter(|i| matches!(i, crate::qasm::Instr::Measure { .. } | crate::qasm::Instr::Reset(_))).count()); + let branches = f64::from(1u32 << mids(a).max(mids(b)).min(10)); + if n <= 16 { (branches * gates, branches * gates * amplitudes) } else { (0.0, 0.0) } + } else { + if n > CHECK_MAX_TABLEAU { + return Some(format!("{n} qubits: wider than a conversion's check compares ({CHECK_MAX_TABLEAU}); `wai-quantum equivalent` runs it on its own")); + } + let clifford = a.clifford().is_ok() && b.clifford().is_ok(); + if clifford { + (0.0, gates * f64::from(n)) + } else if n <= super::equiv::EXACT_MAX_QUBITS && gates * amplitudes * amplitudes <= 2e8 { + (gates * amplitudes, gates * amplitudes * amplitudes) + } else if n <= super::equiv::RANDOM_MAX_QUBITS { + let k = super::equiv::RANDOM_INPUTS as f64; + (k * gates, k * gates * amplitudes) + } else { + (0.0, 0.0) + } + }; + if work > CHECK_WORK || applications > CHECK_APPLICATIONS { + return Some(format!("the check would apply {applications:.0} gates to about {work:.1e} amplitudes, more than a conversion spends ({CHECK_APPLICATIONS:.0} gates, {CHECK_WORK:.0e} amplitudes); `wai-quantum equivalent` runs it on its own")); + } + None +} + +/// The probability of every outcome of a program that branches, from the +/// debugger's branches: `None` when it cannot enumerate them all. +fn distribution(text: &str) -> Option> { + let v = super::debug::branches(&json!({ "qasm": text })).ok()?; + let mut d = std::collections::BTreeMap::new(); + let mut total = 0.0; + for leaf in v["leaves"].as_array()? { + let w = leaf["weight"].as_f64()?; + for f in leaf["final"].as_array()? { + let p = f["p"].as_f64()? * w; + total += p; + *d.entry(f["bits"].as_str()?.to_string()).or_insert(0.0) += p; + } + } + ((total - 1.0).abs() < 1e-9).then_some(d) +} + +// --------------------------------------------------------------------------- +// As JSON, for every surface +// --------------------------------------------------------------------------- + +const B64: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/// Bytes as standard base64, with padding. +pub fn encode_base64(bytes: &[u8]) -> String { + let mut s = String::with_capacity(bytes.len().div_ceil(3) * 4); + for c in bytes.chunks(3) { + let n = (u32::from(c[0]) << 16) | (u32::from(*c.get(1).unwrap_or(&0)) << 8) | u32::from(*c.get(2).unwrap_or(&0)); + s.push(B64[(n >> 18) as usize & 63] as char); + s.push(B64[(n >> 12) as usize & 63] as char); + s.push(if c.len() > 1 { B64[(n >> 6) as usize & 63] as char } else { '=' }); + s.push(if c.len() > 2 { B64[n as usize & 63] as char } else { '=' }); + } + s +} + +/// Standard base64 (padding optional, whitespace ignored) as bytes. +pub fn decode_base64(text: &str) -> Result, String> { + let mut out = Vec::with_capacity(text.len() / 4 * 3); + let (mut acc, mut bits) = (0u32, 0u32); + let mut ended = false; + for (i, ch) in text.bytes().enumerate() { + if ch.is_ascii_whitespace() { + continue; + } + if ch == b'=' { + ended = true; + continue; + } + if ended { + return Err(format!("base64 with data after its padding, at {i}")); + } + let v = B64.iter().position(|&b| b == ch).ok_or_else(|| format!("{:?} at {i} is not base64", ch as char))? as u32; + acc = (acc << 6) | v; + bits += 6; + if bits >= 8 { + bits -= 8; + out.push((acc >> bits) as u8); + acc &= (1 << bits) - 1; + } + } + if bits >= 6 { + return Err("base64 that ends partway through a byte".into()); + } + Ok(out) +} + +fn format_field(v: &Value, key: &str) -> Result, String> { + match v.get(key) { + None | Some(Value::Null) => Ok(None), + Some(Value::String(s)) => Format::parse(s).map(Some).ok_or_else(|| crate::engine::refusal::tag(crate::engine::refusal::BAD_FIELD, format!("{key}: {s} is not a format; the formats are {}", names()))), + Some(_) => Err(crate::engine::refusal::tag(crate::engine::refusal::BAD_FIELD, format!("{key} is a format's name: {}", names()))), + } +} + +fn names() -> String { + Format::all().iter().map(|f| f.name()).collect::>().join(", ") +} + +/// The program a request carries: `program` (text) or `program_base64` +/// (bytes, for a binary format). +fn program_bytes(v: &Value) -> Result, String> { + use crate::engine::refusal::{BAD_FIELD, tag}; + match (v.get("program").filter(|x| !x.is_null()), v.get("program_base64").filter(|x| !x.is_null())) { + (Some(Value::String(s)), None) => Ok(s.clone().into_bytes()), + (None, Some(Value::String(b))) => decode_base64(b).map_err(|e| tag(BAD_FIELD, format!("program_base64: {e}"))), + (Some(_), Some(_)) => Err(tag(BAD_FIELD, "give the program as \"program\" (text) or \"program_base64\" (bytes), not both")), + (None, None) => Err(tag(BAD_FIELD, "no program: give \"program\" (text) or \"program_base64\" (bytes, for jeff)")), + _ => Err(tag(BAD_FIELD, "\"program\" is text and \"program_base64\" is base64 text")), + } +} + +fn notes_json(notes: &[Diagnostic]) -> Value { + Value::Array(notes.iter().map(Diagnostic::to_json).collect()) +} + +/// A conversion as JSON: the request is `{"to", "program" | "program_base64", +/// "from"?, "profile"?, "check"?}` (`from` is found from the bytes when it is +/// not given; `check` is on unless false), and the answer `{"ok", "format", +/// "from", "to", "program" | "program_base64", "qasm", "notes", "read", +/// "written", "check"}`. A refusal is the reader's or writer's diagnostic, with its code +/// last (`… [Q704]`), or the job layer's for a request that does not read. +pub fn convert_json(req: &Value) -> Result { + use crate::engine::refusal::{BAD_FIELD, tag}; + if !req.is_object() { + return Err(tag(BAD_FIELD, "a conversion is a JSON object: {\"to\", \"program\" or \"program_base64\", \"from\", \"profile\", \"check\"}")); + } + let bytes = program_bytes(req)?; + let from = format_field(req, "from")?.unwrap_or_else(|| Format::sniff(&bytes)); + let to = format_field(req, "to")?.ok_or_else(|| tag(BAD_FIELD, format!("\"to\": the format to write, one of {}", names())))?; + let profile = match req.get("profile") { + None | Some(Value::Null) => None, + Some(Value::String(s)) => Some(Profile::parse(s).ok_or_else(|| tag(BAD_FIELD, format!("profile: {s} is not a QIR profile; the profiles are base and adaptive")))?), + Some(_) => return Err(tag(BAD_FIELD, "profile is base or adaptive")), + }; + let check = match req.get("check") { + None | Some(Value::Null) => true, + Some(Value::Bool(b)) => *b, + Some(_) => return Err(tag(BAD_FIELD, "check is true or false")), + }; + let c = convert(from, to, &bytes, profile, check).map_err(String::from)?; + let mut out = json!({ + "ok": true, + "format": FORMAT, + "from": c.from.name(), + "to": c.to.name(), + "qasm": c.qasm, + "notes": notes_json(&c.notes), + "read": c.read, + "written": c.written, + "check": c.check, + }); + if c.to.is_binary() { + out["program_base64"] = json!(encode_base64(&c.output)); + } else { + out["program"] = json!(String::from_utf8(c.output).expect("a text format writes text")); + } + Ok(out) +} + +/// The MCP tool both servers serve the conversion as (`convert_program`), +/// its answer [`convert_json`]'s. +pub fn tool() -> Value { + json!({ + "name": "convert_program", + "title": "Convert a program between formats", + "description": "Carry a program between OpenQASM 3, QIR (LLVM text, read under its base and adaptive profiles, written as QIR 2.0) and jeff (its Cap'n Proto message, as base64). The answer is the program written, the program as OpenQASM 3 (what every other tool takes), each note with its code (a rotation at an arbitrary angle, a custom gate read by its name, what a format leaves out), what was read and written, and the check: the output read back and held to the program by the equivalence ladder. A construct a reader cannot hold exactly is refused with its code (Q700 to Q721) and its place: quote the code, and never guess around it.", + "inputSchema": { + "type": "object", + "required": ["to"], + "properties": { + "program": { "type": "string", "description": "The program's text: OpenQASM or QIR." }, + "program_base64": { "type": "string", "description": "The program's bytes as base64: jeff." }, + "from": { "type": "string", "enum": ["qasm", "qir", "jeff"], "description": "Its format; found from its bytes when not given." }, + "to": { "type": "string", "enum": ["qasm", "qir", "jeff"], "description": "The format to write." }, + "profile": { "type": "string", "enum": ["base", "adaptive"], "description": "QIR's profile to write: by default base when the program's measurements all come at the end, else adaptive." }, + "check": { "type": "boolean", "description": "false: write it without the check." } + } + }, + }) +} + +/// The schema of a job's `source`, as both servers' run tools take it: the +/// program in another format, in place of `qasm`. +pub fn source_schema() -> Value { + json!({ + "type": "object", + "required": ["format"], + "description": "The program in another format, in place of qasm: read to the job's qasm before the job is read (a module that does not read is refused with the reader's code, Q700 to Q721), the request keeping its source.", + "properties": { + "format": { "type": "string", "enum": ["qasm", "qir", "jeff"], "description": "Its format." }, + "program": { "type": "string", "description": "The program's text: QIR (LLVM text) or OpenQASM." }, + "program_base64": { "type": "string", "description": "The program's bytes as base64: jeff." } + } + }) +} + +/// What a run tool's input requires: its program, as `qasm` or as `source`. +pub fn program_required() -> Value { + json!([{ "required": ["qasm"] }, { "required": ["source"] }]) +} + +/// A job whose program is in another format, with `"qasm"` read from its +/// `"source"`: `{"source": {"format", "program" | "program_base64"}}`. A +/// request with no `source` is `None`, to be read as it is, and so is one +/// whose `qasm` is what its `source` reads to (read already); one whose +/// `qasm` is something else is refused, as is a source that does not read. +pub fn expand(req: &Value) -> Result, String> { + use crate::engine::refusal::{BAD_FIELD, NOT_A_JOB, tag}; + let Some(src) = req.get("source").filter(|s| !s.is_null()) else { return Ok(None) }; + let given = req.get("qasm").filter(|q| !q.is_null()); + if !src.is_object() { + return Err(tag(BAD_FIELD, "\"source\" is {\"format\", \"program\" or \"program_base64\"}")); + } + let bytes = program_bytes(src)?; + let format = format_field(src, "format")?.unwrap_or_else(|| Format::sniff(&bytes)); + let imported = read(format, &bytes).map_err(|d| tag(NOT_A_JOB, format!("the source does not read as {}: {d}", format.name())))?; + // A request read already (one a receipt sealed) holds both, alike. + if let Some(q) = given { + if q.as_str() == Some(imported.qasm.as_str()) { + return Ok(None); + } + return Err(tag(BAD_FIELD, "a job gives its program as \"qasm\" or as \"source\": this one gives both, and the qasm is not what the source reads to")); + } + let mut out = req.clone(); + out["qasm"] = json!(imported.qasm); + Ok(Some(out)) +} + +#[cfg(test)] +mod tests; diff --git a/wai/wai-quantum/src/engine/formats/qir.rs b/wai/wai-quantum/src/engine/formats/qir.rs new file mode 100644 index 000000000..bb2d3c767 --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/qir.rs @@ -0,0 +1,1661 @@ +//! QIR, read under its profiles and written from a program. +//! +//! **Reading** takes a module as LLVM text, QIR 1.x (typed pointers, +//! `%Qubit*`) or 2.x (opaque pointers, `ptr`), and holds it to the profile +//! its entry point names (`"qir_profiles"`): +//! +//! - **base**: gates, then measurements, then output records. A branch, a +//! classical instruction, reading a result, or a gate or reset on a qubit +//! after its measurement is refused with the code of what the profile +//! forbids ([`Q704`](crate::qasm::diag::PROFILE_FORBIDS)). +//! - **adaptive**: measurements anywhere, results read as booleans and +//! branched on (forward only), with the `i1` logic the profile allows +//! (`and`, `or`, `xor`, `icmp eq`/`ne`, `select`). An optional capability +//! the module's flags do not declare is refused as forbidden; one they +//! declare and this reader does not take (loops, `switch`, `phi`, +//! IR-defined functions, dynamic qubits or results, arrays, a value +//! computed from a measurement anywhere but a condition) is refused with +//! [`Q705`](crate::qasm::diag::CAPABILITY_NOT_TAKEN). Integer and float +//! computation the flags declare is computed on reading where its operands +//! are constants. +//! +//! The control flow (a tree of forward branches) is written as nested `if` +//! and `else`, each branch running to the block where its paths meet again. +//! Every refusal names its line. The module is read to an OpenQASM 3 +//! program for the one reader every surface uses: result `r` is bit `c[r]`, +//! qubit `i` is `q[i]`. +//! +//! **Angles** are read at their exact doubles (hex or decimal). A +//! Clifford+T module stays Clifford+T and reads to the bit-exact method; +//! an arbitrary-angle rotation reads with a warning +//! ([`Q709`](crate::qasm::diag::ARBITRARY_ANGLE)): the double-precision methods +//! take it, the bit-exact method refuses it. +//! +//! **Writing** gives QIR 2.0 with opaque pointers: the base profile for a +//! program whose measurements all come at the end, the adaptive profile for +//! one that measures in the middle, resets or branches (forward branches on +//! results, written with `__quantum__rt__read_result` and `br` alone). A +//! gate outside the intrinsics written is lowered exactly, up to a global +//! phase; Clifford+T gates are written as Clifford+T intrinsics, and every +//! angle as its exact hex double. + +use super::emit::{self, Circuit, Cond, Stmt}; +use crate::qasm::diag::{self, Diagnostic, Span}; +use crate::qasm::{Instr, Kind, Program}; +use serde_json::{Value, json}; +use std::collections::BTreeMap; + +/// The QIR profiles. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum Profile { + Base, + Adaptive, +} + +impl Profile { + pub fn name(self) -> &'static str { + match self { + Profile::Base => "base", + Profile::Adaptive => "adaptive", + } + } + pub fn parse(s: &str) -> Option { + match s.to_ascii_lowercase().trim_end_matches("_profile") { + "base" => Some(Profile::Base), + "adaptive" => Some(Profile::Adaptive), + _ => None, + } + } + fn attribute(self) -> &'static str { + match self { + Profile::Base => "base_profile", + Profile::Adaptive => "adaptive_profile", + } + } +} + +/// The intrinsics the reader takes, each `(name without its prefix, the +/// OpenQASM gate, parameters, qubits)`. `__body` and `__adj` forms are both +/// read where the gate has an inverse in the table. +const GATES: &[(&str, &str, usize, usize)] = &[ + ("x", "x", 0, 1), + ("y", "y", 0, 1), + ("z", "z", 0, 1), + ("h", "h", 0, 1), + ("s", "s", 0, 1), + ("t", "t", 0, 1), + ("sx", "sx", 0, 1), + ("rx", "rx", 1, 1), + ("ry", "ry", 1, 1), + ("rz", "rz", 1, 1), + ("r1", "p", 1, 1), + ("cnot", "cx", 0, 2), + ("cx", "cx", 0, 2), + ("cy", "cy", 0, 2), + ("cz", "cz", 0, 2), + ("swap", "swap", 0, 2), + ("ccx", "ccx", 0, 3), + ("ccz", "ccz", 0, 3), + ("rxx", "rxx", 1, 2), + ("ryy", "ryy", 1, 2), + ("rzz", "rzz", 1, 2), +]; + +/// What a module was read as. +#[derive(Clone, Debug, PartialEq)] +pub struct Reading { + /// The program, as OpenQASM 3. + pub qasm: String, + /// The warnings and notes, each with its code and its line in the module. + pub notes: Vec, + /// The profile, the version, the entry point, the qubits and results, the + /// capabilities the flags declare and the results recorded as output. + pub summary: Value, +} + +fn err(code: &'static str, line: usize, msg: impl Into) -> Diagnostic { + Diagnostic::new(code, (line > 0).then_some(Span { line: line as u32, column: 1, length: 0 }), msg) +} + +// --------------------------------------------------------------------------- +// The module's text +// --------------------------------------------------------------------------- + +/// A line without its comment, and its number. +#[derive(Clone, Debug)] +struct Line<'a> { + no: usize, + text: &'a str, +} + +/// `s` with a `;` comment removed (outside quotes). +fn strip_comment(s: &str) -> &str { + let mut quoted = false; + for (i, ch) in s.char_indices() { + match ch { + '"' => quoted = !quoted, + ';' if !quoted => return &s[..i], + _ => {} + } + } + s +} + +#[derive(Clone, Debug, Default)] +struct Func<'a> { + name: String, + line: usize, + attrs: Vec, + /// Its blocks, in order: (label, its lines). + blocks: Vec<(String, Vec>)>, +} + +#[derive(Default)] +struct Module<'a> { + defines: Vec>, + declares: BTreeMap>, + groups: BTreeMap)>>, + metadata: BTreeMap, + flags: Vec, + typed_pointers: bool, +} + +/// `#0`, `#1`, … in `s`. +fn group_refs(s: &str) -> Vec { + s.split_whitespace().filter(|t| t.starts_with('#') && t[1..].bytes().all(|b| b.is_ascii_digit()) && t.len() > 1).map(String::from).collect() +} + +/// The attributes in an attribute group's braces: `"name"` or `"name"="value"`, and bare words. +fn attributes(s: &str) -> Vec<(String, Option)> { + let mut out = Vec::new(); + let b = s.as_bytes(); + let mut i = 0; + while i < b.len() { + match b[i] { + b'"' => { + let end = s[i + 1..].find('"').map_or(b.len(), |e| i + 1 + e); + let name = s[i + 1..end].to_string(); + i = end + 1; + if i < b.len() && b[i] == b'=' && i + 1 < b.len() && b[i + 1] == b'"' { + let vend = s[i + 2..].find('"').map_or(b.len(), |e| i + 2 + e); + out.push((name, Some(s[i + 2..vend].to_string()))); + i = vend + 1; + } else { + out.push((name, None)); + } + } + c if c.is_ascii_alphabetic() => { + let end = s[i..].find(|c: char| c.is_whitespace() || c == '}').map_or(b.len(), |e| i + e); + out.push((s[i..end].to_string(), None)); + i = end; + } + _ => i += 1, + } + } + out +} + +fn parse_module(src: &str) -> Result, Diagnostic> { + let mut m = Module::default(); + let mut current: Option = None; + for (i, raw) in src.lines().enumerate() { + let no = i + 1; + let text = strip_comment(raw).trim(); + if text.is_empty() { + continue; + } + if let Some(f) = current.as_mut() { + if text == "}" { + m.defines.push(current.take().expect("a function")); + continue; + } + // A label: `name:` (possibly quoted), the rest a comment. + if let Some(label) = text.strip_suffix(':').filter(|l| !l.contains(' ') && !l.contains('=')) { + f.blocks.push((label.trim_matches('"').to_string(), Vec::new())); + continue; + } + if f.blocks.is_empty() { + // The entry block may have no label. + f.blocks.push(("".into(), Vec::new())); + } + f.blocks.last_mut().expect("a block").1.push(Line { no, text }); + continue; + } + if text.starts_with("define ") { + let at = text.find('@').ok_or_else(|| err(diag::FORMAT_UNREAD, no, "a `define` with no function name"))?; + let name: String = text[at + 1..].chars().take_while(|&c| c != '(').collect(); + let close = text.rfind(')').unwrap_or(text.len()); + let mut attrs = group_refs(&text[close..]); + attrs.extend(attributes(&text[close..]).into_iter().filter(|(n, _)| n == "entry_point").map(|(n, _)| n)); + if !text.ends_with('{') { + return Err(err(diag::FORMAT_UNREAD, no, "a `define` whose body does not open on its line")); + } + current = Some(Func { name: name.trim_matches('"').to_string(), line: no, attrs, blocks: Vec::new() }); + continue; + } + if text.starts_with("declare ") { + if let Some(at) = text.find('@') { + let name: String = text[at + 1..].chars().take_while(|&c| c != '(').collect(); + let close = text.rfind(')').unwrap_or(text.len()); + m.declares.insert(name, group_refs(&text[close..])); + } + continue; + } + if let Some(rest) = text.strip_prefix("attributes ") { + let (id, body) = rest.split_once('=').ok_or_else(|| err(diag::FORMAT_UNREAD, no, "an attribute group that does not read"))?; + let body = body.trim().trim_start_matches('{').trim_end_matches('}'); + m.groups.insert(id.trim().to_string(), attributes(body)); + continue; + } + if let Some(rest) = text.strip_prefix("!llvm.module.flags") { + m.flags = rest.split(['{', '}', ',', '=']).map(str::trim).filter(|t| t.starts_with('!') && t.len() > 1).map(String::from).collect(); + continue; + } + if text.starts_with('!') { + if let Some((id, body)) = text.split_once('=') { + m.metadata.insert(id.trim().to_string(), body.trim().to_string()); + } + continue; + } + if text.contains("%Qubit*") || text.contains("%Result*") || text.starts_with("%Qubit") || text.starts_with("%Result") { + m.typed_pointers = true; + } + // Globals (output labels), types, `source_filename`, `target …`: nothing to read. + } + if current.is_some() { + return Err(err(diag::FORMAT_UNREAD, src.lines().count(), "a function body that is not closed with `}`")); + } + Ok(m) +} + +/// A module flag: `!{i32 1, !"name", }`. +fn flag<'m>(m: &'m Module, name: &str) -> Option<&'m str> { + let want = format!("!\"{name}\""); + m.flags.iter().filter_map(|r| m.metadata.get(r)).find(|body| body.contains(&want)).and_then(|body| body.trim_start_matches('!').trim_matches(['{', '}']).split(',').nth(2).map(str::trim)) +} + +/// The strings of a metadata tuple a flag refers to (`!10 = !{!"i32", !"i64"}`). +fn flag_strings(m: &Module, name: &str) -> Option> { + let v = flag(m, name)?; + let body = if v.starts_with("!{") { v.to_string() } else { m.metadata.get(v)?.clone() }; + Some(body.trim_start_matches('!').trim_matches(['{', '}']).split(',').map(|s| s.trim().trim_start_matches('!').trim_matches('"').to_string()).filter(|s| !s.is_empty()).collect()) +} + +fn flag_bool(m: &Module, name: &str) -> Option { + flag(m, name).map(|v| matches!(v.split_whitespace().last(), Some("true" | "1"))) +} + +fn flag_int(m: &Module, name: &str) -> Option { + flag(m, name).and_then(|v| v.split_whitespace().last()).and_then(|v| match v { + "true" => Some(1), + "false" => Some(0), + n => n.parse().ok(), + }) +} + +// --------------------------------------------------------------------------- +// Instructions and their operands +// --------------------------------------------------------------------------- + +/// Splits `s` at commas outside brackets, parentheses and quotes. +fn split_top(s: &str) -> Vec<&str> { + let mut out = Vec::new(); + let (mut depth, mut quoted, mut start) = (0i32, false, 0); + for (i, ch) in s.char_indices() { + match ch { + '"' => quoted = !quoted, + '(' | '[' | '{' | '<' if !quoted => depth += 1, + ')' | ']' | '}' | '>' if !quoted => depth -= 1, + ',' if !quoted && depth == 0 => { + out.push(s[start..i].trim()); + start = i + 1; + } + _ => {} + } + } + let last = s[start..].trim(); + if !last.is_empty() { + out.push(last); + } + out +} + +/// The value of an argument written `TYPE [attributes] VALUE`. +fn arg_value(arg: &str) -> &str { + let toks: Vec<(usize, &str)> = arg.split_whitespace().scan(0usize, |pos, t| { + let at = arg[*pos..].find(t).map_or(*pos, |i| *pos + i); + *pos = at + t.len(); + Some((at, t)) + }).collect(); + let mut i = 1; // the type is the first token + while i < toks.len() { + let t = toks[i].1; + let value_start = t == "null" + || t == "true" + || t == "false" + || t == "undef" + || t == "poison" + || t.starts_with("inttoptr") + || t.starts_with("getelementptr") + || t.starts_with('%') + || t.starts_with('@') + || t.starts_with('-') + || t.starts_with(|c: char| c.is_ascii_digit()); + if value_start { + return arg[toks[i].0..].trim(); + } + if t == "align" { + i += 1; + } + i += 1; + } + "" +} + +/// A value an operand holds on reading. +#[derive(Clone, Debug, PartialEq)] +enum Val { + /// A boolean computed from measured results, with the measurement each + /// result held when it was read. + Bool(Cond, Vec<(u32, u32)>), + Int(i128), + Float(f64), + /// The result `One` (QIR 1.x's `__quantum__rt__result_get_one`). + One, +} + +/// A pointer operand's constant index: `null`, `inttoptr (i64 N to …)`. +fn pointer_index(v: &str) -> Option { + let v = v.trim(); + if v == "null" { + return Some(0); + } + let inner = v.strip_prefix("inttoptr")?.trim().trim_start_matches('(').trim_end_matches(')'); + let mut t = inner.split_whitespace(); + if t.next()? != "i64" { + return None; + } + t.next()?.parse().ok() +} + +/// A floating-point literal: decimal, or LLVM's exact hex form (`0x` and the +/// double's 64 bits). +fn float_literal(v: &str) -> Option { + let v = v.trim(); + if let Some(h) = v.strip_prefix("0x") { + if h.len() == 16 && h.bytes().all(|b| b.is_ascii_hexdigit()) { + return u64::from_str_radix(h, 16).ok().map(f64::from_bits); + } + return None; + } + v.parse::().ok().filter(|x| x.is_finite()) +} + +/// The intrinsic a call names, without its prefix, and which form. +enum Callee<'a> { + Qis(&'a str), + Rt(&'a str), + Other(&'a str), +} + +fn callee(name: &str) -> Callee<'_> { + if let Some(r) = name.strip_prefix("__quantum__qis__") { + Callee::Qis(r) + } else if let Some(r) = name.strip_prefix("__quantum__rt__") { + Callee::Rt(r) + } else { + Callee::Other(name) + } +} + +/// A call: `[%x =] [tail] call TYPE @name(ARGS) …`. +struct Call<'a> { + def: Option<&'a str>, + name: &'a str, + args: Vec<&'a str>, +} + +fn parse_call(text: &str) -> Option> { + let (def, rest) = match text.split_once('=') { + Some((d, r)) if d.trim().starts_with('%') && !d.contains('(') => (Some(d.trim()), r.trim()), + _ => (None, text), + }; + let rest = rest.trim_start_matches("tail ").trim_start_matches("notail ").trim_start_matches("musttail ").trim(); + let rest = rest.strip_prefix("call ")?; + let at = rest.find('@')?; + let after = &rest[at + 1..]; + let open = after.find('(')?; + let name = after[..open].trim_matches('"'); + // The arguments run to the matching parenthesis. + let mut depth = 0; + let mut end = None; + for (i, ch) in after[open..].char_indices() { + match ch { + '(' => depth += 1, + ')' => { + depth -= 1; + if depth == 0 { + end = Some(open + i); + break; + } + } + _ => {} + } + } + let args = split_top(&after[open + 1..end?]); + Some(Call { def, name, args }) +} + +// --------------------------------------------------------------------------- +// Reading +// --------------------------------------------------------------------------- + +/// What the profile and the module's flags allow. +#[derive(Clone, Debug)] +struct Allowed { + profile: Profile, + ints: Vec, + floats: Vec, + ir_functions: bool, + backwards: i64, + multi_target: bool, + multi_return: bool, + dyn_qubits: bool, + dyn_results: bool, + arrays: bool, +} + +/// The state along one path through the blocks. +#[derive(Clone, Debug, Default)] +struct Path { + env: BTreeMap, + /// How many times each result has been measured on this path. + gens: Vec, + /// Each qubit: measured (base profile), touched by a gate. + measured: Vec, + touched: Vec, +} + +struct Reader<'m, 'a> { + m: &'m Module<'a>, + f: &'m Func<'a>, + allowed: Allowed, + n_qubits: u32, + n_results: u32, + declared_qubits: bool, + declared_results: bool, + labels: BTreeMap<&'m str, usize>, + notes: Vec, + records: Vec, + arbitrary: usize, + emitted: usize, + visits: usize, + instructions: usize, + copied: usize, + used_qubits: u32, + used_results: u32, +} + +/// The most statements the branches are written out to (a block reached by +/// many paths is written once per path). +pub const MAX_STATEMENTS: usize = 1 << 20; +/// The most blocks visited, over every path. +pub const MAX_VISITS: usize = 1 << 20; +/// The most instructions run, over every path. +pub const MAX_INSTRUCTIONS: usize = 1 << 22; +/// The deepest branches nest. +pub const MAX_NESTING: usize = 64; +/// The most a walk copies of the state along its paths (a result's count, a +/// qubit's flags, each value and its terms), over every branch. +pub const MAX_COPIED: usize = 1 << 26; +/// The largest module read, in bytes. +pub const MAX_BYTES: usize = 16 << 20; + +enum Term<'a> { + Ret, + Br(&'a str), + CondBr(&'a str, &'a str, &'a str), +} + +impl<'m, 'a> Reader<'m, 'a> { + fn forbidden(&self, line: usize, what: &str) -> Diagnostic { + err(diag::PROFILE_FORBIDS, line, format!("the {} profile forbids {what}", self.allowed.profile.name())) + } + + fn not_taken(&self, line: usize, what: &str) -> Diagnostic { + err(diag::CAPABILITY_NOT_TAKEN, line, format!("{what}: the module may use it, and this reader does not take it")) + } + + fn qubit(&mut self, v: &str, line: usize) -> Result { + let i = pointer_index(v).ok_or_else(|| { + if v.starts_with('%') { + if self.allowed.dyn_qubits { self.not_taken(line, "a qubit that is not a constant index (dynamic qubit management)") } else { err(diag::OPERAND, line, format!("the qubit {v} is not a constant index")) } + } else { + err(diag::OPERAND, line, format!("the qubit {v} is not a constant index")) + } + })?; + if self.declared_qubits && i >= u64::from(self.n_qubits) { + return Err(err(diag::OPERAND, line, format!("qubit {i} is past required_num_qubits={}", self.n_qubits))); + } + if i >= u64::from(crate::qasm::MAX_QUBITS) { + return Err(err(diag::OPERAND, line, format!("qubit {i}: more qubits than the reader takes"))); + } + self.used_qubits = self.used_qubits.max(i as u32 + 1); + Ok(i as u32) + } + + fn result(&mut self, v: &str, line: usize) -> Result { + let i = pointer_index(v).ok_or_else(|| { + if v.starts_with('%') && self.allowed.dyn_results { + self.not_taken(line, "a result that is not a constant index (dynamic result management)") + } else { + err(diag::OPERAND, line, format!("the result {v} is not a constant index")) + } + })?; + if self.declared_results && i >= u64::from(self.n_results) { + return Err(err(diag::OPERAND, line, format!("result {i} is past required_num_results={}", self.n_results))); + } + if i >= u64::from(crate::qasm::MAX_QUBITS) { + return Err(err(diag::OPERAND, line, format!("result {i}: more results than the reader takes"))); + } + self.used_results = self.used_results.max(i as u32 + 1); + Ok(i as u32) + } + + fn value(&self, v: &str, st: &Path, line: usize) -> Result { + let v = v.trim(); + if v.starts_with('%') { + return st.env.get(v).cloned().ok_or_else(|| err(diag::OPERAND, line, format!("{v} is not defined on every path that reaches this line, or holds what the reader does not keep (a pointer)"))); + } + Ok(match v { + "true" => Val::Bool(Cond::Const(true), vec![]), + "false" => Val::Bool(Cond::Const(false), vec![]), + _ => { + if let Ok(i) = v.parse::() { + Val::Int(i) + } else if let Some(f) = float_literal(v) { + Val::Float(f) + } else { + return Err(err(diag::OPERAND, line, format!("{v}: not a value the reader reads"))); + } + } + }) + } + + /// A gate's angle: a finite double known on reading. + fn angle(&mut self, arg: &str, st: &Path, line: usize) -> Result { + let v = arg_value(arg); + match self.value(v, st, line)? { + Val::Float(f) if f.is_finite() => Ok(f), + Val::Int(i) => Ok(i as f64), + _ => Err(err(diag::OPERAND, line, format!("the angle {v} is not a finite number known on reading"))), + } + } + + fn bool(&self, arg: &str, st: &Path, line: usize) -> Result<(Cond, Vec<(u32, u32)>), Diagnostic> { + match self.value(arg_value(arg), st, line)? { + Val::Bool(c, d) => Ok((c, d)), + Val::Int(i) => Ok((Cond::Const(i & 1 == 1), vec![])), + _ => Err(err(diag::OPERAND, line, format!("{arg}: not a boolean"))), + } + } + + /// One instruction of a block (not its terminator). + fn instruction(&mut self, l: &Line, st: &mut Path, out: &mut Vec) -> Result<(), Diagnostic> { + let t = l.text; + let line = l.no; + if let Some(call) = parse_call(t) { + return self.call(&call, line, st, out); + } + let Some((def, rhs)) = t.split_once('=').filter(|(d, _)| d.trim().starts_with('%')) else { + return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("`{t}`: an instruction the reader does not know"))); + }; + let def = def.trim().to_string(); + let rhs = rhs.trim(); + let op = rhs.split_whitespace().next().unwrap_or(""); + let is_base = self.allowed.profile == Profile::Base; + match op { + "phi" => return Err(if is_base { self.forbidden(line, "a `phi`") } else { self.not_taken(line, "a `phi` (a value that depends on the path taken)") }), + "alloca" | "load" | "store" | "getelementptr" | "extractvalue" | "insertvalue" => { + return Err(if self.allowed.arrays && !is_base { self.not_taken(line, &format!("`{op}` (arrays)")) } else { self.forbidden(line, &format!("`{op}`")) }); + } + _ => {} + } + if is_base { + return Err(self.forbidden(line, &format!("the classical instruction `{op}`"))); + } + let rest = rhs[op.len()..].trim(); + // The type is the first word after any flags. + let flags = ["nuw", "nsw", "exact", "fast", "nnan", "ninf", "nsz", "arcp", "contract", "afn", "reassoc", "disjoint"]; + let mut words = rest.split_whitespace().skip_while(|w| flags.contains(w)); + let (pred, ty) = if op == "icmp" || op == "fcmp" { + let p = words.next().unwrap_or(""); + (p, words.next().unwrap_or("")) + } else { + ("", words.next().unwrap_or("")) + }; + let after_ty = rest.find(ty).map_or("", |i| &rest[i + ty.len()..]).trim(); + let operands = split_top(after_ty); + let val = |r: &Self, s: &str| r.value(s.split(" to ").next().unwrap_or(s), st, line); + // `i1` logic: allowed in every adaptive program. + let bool_select = op == "select" && split_top(rest).get(1).is_some_and(|a| a.trim_start().starts_with("i1 ")); + if ty == "i1" && matches!(op, "and" | "or" | "xor" | "icmp") || bool_select { + let get = |r: &Self, s: &str| -> Result<(Cond, Vec<(u32, u32)>), Diagnostic> { + let s = s.trim().trim_start_matches("i1 ").trim(); + match r.value(s, st, line)? { + Val::Bool(c, d) => Ok((c, d)), + Val::Int(i) => Ok((Cond::Const(i & 1 == 1), vec![])), + _ => Err(err(diag::OPERAND, line, format!("{s}: not a boolean"))), + } + }; + let v = match op { + "select" => { + let parts = split_top(&rest[rest.find("i1").unwrap_or(0) + 2..]); + if parts.len() != 3 { + return Err(err(diag::FORMAT_UNREAD, line, "a `select` that does not read")); + } + let (c, mut d) = get(self, parts[0])?; + let (a, da) = get(self, parts[1].trim_start_matches("i1").trim())?; + let (b, db) = get(self, parts[2].trim_start_matches("i1").trim())?; + d.extend(da); + d.extend(db); + (Cond::or(Cond::and(c.clone(), a), Cond::and(Cond::not(c), b)), d) + } + _ => { + if operands.len() != 2 { + return Err(err(diag::FORMAT_UNREAD, line, format!("`{op}` with {} operands", operands.len()))); + } + let (a, mut d) = get(self, operands[0])?; + let (b, db) = get(self, operands[1])?; + d.extend(db); + let c = match (op, pred) { + ("and", _) => Cond::and(a, b), + ("or", _) => Cond::or(a, b), + ("xor", _) | ("icmp", "ne") => Cond::xor(a, b), + ("icmp", "eq") => Cond::not(Cond::xor(a, b)), + _ => return Err(self.not_taken(line, &format!("`icmp {pred}` on booleans"))), + }; + (c, d) + } + }; + if !emit::cond_fits(&v.0) { + return Err(self.not_taken(line, &format!("a condition of more than {} terms or nested deeper than {}", emit::MAX_COND, emit::MAX_COND_DEPTH))); + } + let mut deps = v.1; + deps.sort_unstable(); + deps.dedup(); + st.env.insert(def, Val::Bool(v.0, deps)); + return Ok(()); + } + // Integer and float computation, where the flags declare its type. + let float_op = matches!(op, "fadd" | "fsub" | "fmul" | "fdiv" | "fneg" | "fcmp" | "fpext" | "fptrunc" | "sitofp" | "uitofp"); + let int_op = matches!(op, "add" | "sub" | "mul" | "udiv" | "sdiv" | "urem" | "srem" | "and" | "or" | "xor" | "shl" | "lshr" | "ashr" | "icmp" | "zext" | "sext" | "trunc" | "fptosi" | "fptoui"); + if !float_op && !int_op { + return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("`{op}`: an instruction the adaptive profile does not list"))); + } + let (declared, kind) = if float_op { (&self.allowed.floats, "floats") } else { (&self.allowed.ints, "integers") }; + // A conversion is computation on the type it gives. + let ty_name = match rest.rsplit_once(" to ") { + Some((_, to)) if matches!(op, "zext" | "sext" | "trunc" | "fpext" | "fptrunc" | "sitofp" | "uitofp" | "fptosi" | "fptoui") => to.trim(), + _ => ty.trim_end_matches(','), + }; + if !declared.iter().any(|d| d == ty_name) { + return Err(self.forbidden(line, &format!("computation on {kind} of type {ty_name} (its module flags do not declare it)"))); + } + let vals: Vec = operands.iter().map(|o| val(self, o)).collect::>().map_err(|_| self.not_taken(line, "a classical value computed from a measurement, used anywhere but a condition"))?; + let f = |v: &Val| match v { + Val::Float(f) => Some(*f), + Val::Int(i) => Some(*i as f64), + _ => None, + }; + let i = |v: &Val| match v { + Val::Int(i) => Some(*i), + Val::Bool(Cond::Const(b), _) => Some(i128::from(*b)), + _ => None, + }; + let need = |o: Option| o.ok_or_else(|| self.not_taken(line, &format!("`{op}` on a value not known on reading"))); + let v = match op { + "fadd" | "fsub" | "fmul" | "fdiv" => { + let (Some(a), Some(b)) = (vals.first().and_then(f), vals.get(1).and_then(f)) else { return Err(need(None).unwrap_err()) }; + Val::Float(match op { + "fadd" => a + b, + "fsub" => a - b, + "fmul" => a * b, + _ => a / b, + }) + } + "fneg" => need(vals.first().and_then(f).map(|a| Val::Float(-a)))?, + "fpext" | "sitofp" | "uitofp" => need(vals.first().and_then(f).map(Val::Float))?, + "fptrunc" => need(vals.first().and_then(f).map(|a| Val::Float(f64::from(a as f32))))?, + "fptosi" | "fptoui" => need(vals.first().and_then(f).map(|a| Val::Int(a as i128)))?, + "zext" | "sext" | "trunc" => need(vals.first().and_then(i).map(Val::Int))?, + "icmp" => { + let (Some(a), Some(b)) = (vals.first().and_then(i), vals.get(1).and_then(i)) else { return Err(need(None).unwrap_err()) }; + let r = match pred { + "eq" => a == b, + "ne" => a != b, + "slt" | "ult" => a < b, + "sle" | "ule" => a <= b, + "sgt" | "ugt" => a > b, + "sge" | "uge" => a >= b, + _ => return Err(err(diag::FORMAT_UNREAD, line, format!("`icmp {pred}`"))), + }; + Val::Bool(Cond::Const(r), vec![]) + } + _ => { + let (Some(a), Some(b)) = (vals.first().and_then(i), vals.get(1).and_then(i)) else { return Err(need(None).unwrap_err()) }; + Val::Int(match op { + "add" => a.wrapping_add(b), + "sub" => a.wrapping_sub(b), + "mul" => a.wrapping_mul(b), + "udiv" | "sdiv" | "urem" | "srem" if b == 0 => return Err(err(diag::OPERAND, line, "division by zero")), + "udiv" | "sdiv" => a / b, + "urem" | "srem" => a % b, + "and" => a & b, + "or" => a | b, + "xor" => a ^ b, + "shl" => u32::try_from(b).ok().and_then(|k| a.checked_shl(k)).unwrap_or(0), + _ => u32::try_from(b).ok().and_then(|k| a.checked_shr(k)).unwrap_or(0), + }) + } + }; + st.env.insert(def, v); + Ok(()) + } + + fn call(&mut self, c: &Call, line: usize, st: &mut Path, out: &mut Vec) -> Result<(), Diagnostic> { + let base = self.allowed.profile == Profile::Base; + match callee(c.name) { + Callee::Qis(rest) => { + let (stem, form) = match rest.rsplit_once("__") { + Some((s, f)) => (s, f), + None => (rest, "body"), + }; + match stem { + "mz" | "m" | "mresetz" => { + if c.args.len() != 2 { + return Err(err(diag::FORMAT_UNREAD, line, format!("{} takes a qubit and a result", c.name))); + } + let q = self.qubit(arg_value(c.args[0]), line)?; + let r = self.result(arg_value(c.args[1]), line)?; + if base && *st.measured(q) { + return Err(self.forbidden(line, &format!("measuring qubit {q} twice"))); + } + *st.measured(q) = true; + *st.generation(r) += 1; + out.push(Stmt::Measure { qubit: q, bit: r }); + if stem == "mresetz" { + if base { + return Err(self.forbidden(line, "a reset after a measurement (`mresetz`)")); + } + out.push(Stmt::Reset(q)); + } + self.emitted += 1; + } + "reset" => { + let q = self.qubit(arg_value(c.args.first().copied().unwrap_or("")), line)?; + if base && (*st.touched(q) || *st.measured(q)) { + return Err(self.forbidden(line, &format!("a reset of qubit {q} after a gate or a measurement"))); + } + if !base || *st.touched(q) { + out.push(Stmt::Reset(q)); + } + *st.measured(q) = false; + self.emitted += 1; + } + "read_result" => return self.read_result(c, line, st), + "barrier" => { + self.notes.push(err(diag::WRITE_DROPPED, line, "a barrier changes nothing computed: left out")); + } + _ => { + let Some(&(_, name, n_params, n_qubits)) = GATES.iter().find(|g| g.0 == stem) else { + return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("{}: a quantum intrinsic the reader does not know; it reads {}", c.name, GATES.iter().map(|g| g.0).collect::>().join(", ")))); + }; + let inv = match form { + "body" => false, + "adj" => true, + "ctl" | "ctladj" => return Err(self.not_taken(line, &format!("{}: a controlled form, whose controls come in an array", c.name))), + f => return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("{}: the form `{f}` is not one the reader knows", c.name))), + }; + if c.args.len() != n_params + n_qubits { + return Err(err(diag::FORMAT_UNREAD, line, format!("{} takes {} arguments, not {}", c.name, n_params + n_qubits, c.args.len()))); + } + let mut params = Vec::new(); + for a in &c.args[..n_params] { + params.push(self.angle(a, st, line)?); + } + let mut qubits = Vec::new(); + for a in &c.args[n_params..] { + let q = self.qubit(arg_value(a), line)?; + if qubits.contains(&q) { + return Err(err(diag::OPERAND, line, format!("{}: qubit {q} named twice", c.name))); + } + if base && *st.measured(q) { + return Err(self.forbidden(line, &format!("a gate on qubit {q} after its measurement"))); + } + qubits.push(q); + } + for &q in &qubits { + *st.touched(q) = true; + } + for &p in ¶ms { + if !is_dyadic(p) { + self.arbitrary += 1; + } + } + let inv = inv && !matches!(name, "x" | "y" | "z" | "h" | "cx" | "cy" | "cz" | "swap" | "ccx" | "ccz"); + let stmts = portable(name, ¶ms, &qubits, inv); + self.emitted += stmts.len(); + out.extend(stmts); + } + } + } + Callee::Rt(rest) => match rest { + "initialize" => {} + "read_result" => return self.read_result(c, line, st), + "result_get_one" => { + if base { + return Err(self.forbidden(line, "reading a result")); + } + if let Some(d) = c.def { + st.env.insert(d.to_string(), Val::One); + } + } + "result_get_zero" => return Err(self.not_taken(line, "__quantum__rt__result_get_zero")), + "result_equal" => { + if base { + return Err(self.forbidden(line, "comparing results")); + } + let vals: Vec<&str> = c.args.iter().map(|a| arg_value(a)).collect(); + let (r, one) = match vals.as_slice() { + [a, b] if st.env.get(*b) == Some(&Val::One) => (*a, true), + [a, b] if st.env.get(*a) == Some(&Val::One) => (*b, true), + _ => ("", false), + }; + if !one { + return Err(self.not_taken(line, "__quantum__rt__result_equal between two measured results")); + } + let r = self.result(r, line)?; + if let Some(d) = c.def { + let g = *st.generation(r); + st.env.insert(d.to_string(), Val::Bool(Cond::Bit(r), vec![(r, g)])); + } + } + "result_record_output" => { + let r = self.result(arg_value(c.args.first().copied().unwrap_or("")), line)?; + self.records.push(r); + } + "tuple_record_output" | "array_record_output" => {} + "bool_record_output" | "int_record_output" | "double_record_output" | "float_record_output" => { + self.notes.push(err(diag::WRITE_DROPPED, line, format!("{}: a classical value recorded as output is not carried; the program's bits are its results", c.name))); + } + "qubit_allocate" | "qubit_release" | "qubit_array_allocate" | "qubit_array_release" | "result_allocate" | "result_release" | "result_array_allocate" | "result_array_release" | "result_array_record_output" => { + let declared = if rest.starts_with("qubit") { self.allowed.dyn_qubits } else if rest.contains("array") { self.allowed.arrays } else { self.allowed.dyn_results }; + return Err(if declared && !base { self.not_taken(line, &format!("{} (dynamic allocation)", c.name)) } else { self.forbidden(line, &format!("{} (its module flags do not declare dynamic management)", c.name)) }); + } + _ => return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("{}: a runtime function the reader does not take", c.name))), + }, + Callee::Other(name) => { + let defined = self.m.defines.iter().any(|f| f.name == name); + return Err(if defined { + if self.allowed.ir_functions && !base { self.not_taken(line, &format!("a call of the IR-defined function @{name}")) } else { self.forbidden(line, &format!("a call of the IR-defined function @{name} (its module flags do not declare IR functions)")) } + } else { + err(diag::INSTRUCTION_UNKNOWN, line, format!("@{name}: a function the reader does not know")) + }); + } + } + Ok(()) + } + + fn read_result(&mut self, c: &Call, line: usize, st: &mut Path) -> Result<(), Diagnostic> { + if self.allowed.profile == Profile::Base { + return Err(self.forbidden(line, "reading a result")); + } + let r = self.result(arg_value(c.args.first().copied().unwrap_or("")), line)?; + let g = *st.generation(r); + if let Some(d) = c.def { + st.env.insert(d.to_string(), Val::Bool(Cond::Bit(r), vec![(r, g)])); + } + Ok(()) + } + + fn terminator<'b>(&self, b: usize) -> Result<(Term<'b>, usize), Diagnostic> + where + 'a: 'b, + 'm: 'b, + { + let f: &'m Func<'a> = self.f; + let (label, lines) = &f.blocks[b]; + let Some(last) = lines.last() else { + return Err(err(diag::FORMAT_UNREAD, self.f.line, format!("the block {label} is empty"))); + }; + let t: &'b str = last.text; + let line = last.no; + let label_of = |s: &'b str| -> &'b str { s.trim().trim_start_matches("label").trim().trim_start_matches('%').trim_matches('"') }; + if t.starts_with("ret") { + return Ok((Term::Ret, line)); + } + if let Some(r) = t.strip_prefix("br ") { + let parts = split_top(r); + return match parts.as_slice() { + [l] => Ok((Term::Br(label_of(l)), line)), + [c, a, b] => Ok((Term::CondBr(c.trim(), label_of(a), label_of(b)), line)), + _ => Err(err(diag::FORMAT_UNREAD, line, "a `br` that does not read")), + }; + } + if t.starts_with("switch ") { + return Err(if self.allowed.multi_target && self.allowed.profile == Profile::Adaptive { self.not_taken(line, "`switch` (multiple-target branching)") } else { self.forbidden(line, "`switch` (its module flags do not declare multiple-target branching)") }); + } + if t.starts_with("unreachable") || t.starts_with("indirectbr") || t.starts_with("invoke") { + return Err(err(diag::INSTRUCTION_UNKNOWN, line, format!("`{t}`: a terminator the reader does not take"))); + } + Err(err(diag::FORMAT_UNREAD, line, format!("the block {label} does not end with `br` or `ret`"))) + } + + fn block(&self, label: &str, line: usize) -> Result { + self.labels.get(label).copied().ok_or_else(|| err(diag::FORMAT_UNREAD, line, format!("a branch to %{label}, which is not a block of the entry point"))) + } + + /// Writes the blocks from `b` until `stop` (or a return) into `out`. + fn emit_seq(&mut self, mut b: usize, stop: usize, ipdom: &[usize], st: &mut Path, out: &mut Vec, depth: usize) -> Result<(), Diagnostic> { + let exit = self.f.blocks.len(); + let f: &'m Func<'a> = self.f; + if depth > MAX_NESTING { + return Err(self.not_taken(f.blocks[b].1.first().map_or(f.line, |l| l.no), &format!("branches nested deeper than {MAX_NESTING}"))); + } + loop { + if b == stop { + return Ok(()); + } + self.visits += 1; + let lines = &f.blocks[b].1; + self.instructions = self.instructions.saturating_add(lines.len()); + if self.visits > MAX_VISITS || self.emitted > MAX_STATEMENTS || self.instructions > MAX_INSTRUCTIONS { + return Err(self.not_taken(lines.first().map_or(f.line, |l| l.no), &format!("branches that would be written out to more than the reader holds ({MAX_STATEMENTS} statements, {MAX_INSTRUCTIONS} instructions run over every path)"))); + } + for l in &lines[..lines.len() - 1] { + self.instruction(l, st, out)?; + } + let (term, line) = self.terminator(b)?; + match term { + Term::Ret => return Ok(()), + Term::Br(l) => b = self.block(l, line)?, + Term::CondBr(v, t, f) => { + if self.allowed.profile == Profile::Base { + return Err(self.forbidden(line, "a conditional branch")); + } + let (cond, deps) = self.bool(v, st, line)?; + for (r, g) in deps { + if *st.generation(r) != g { + return Err(err(diag::STALE_READ, line, format!("{v} reads result {r}, which is measured again before this branch: the branch would read the later measurement"))); + } + } + let (tb, fb) = (self.block(t, line)?, self.block(f, line)?); + let j = ipdom[b]; + match cond { + Cond::Const(c) => { + b = if c { tb } else { fb }; + continue; + } + cond => { + self.copied = self.copied.saturating_add(2 * st.weight()); + if self.copied > MAX_COPIED { + return Err(self.not_taken(line, &format!("branches whose paths would copy more state than the reader does ({MAX_COPIED})"))); + } + let (mut st_t, mut st_f) = (st.clone(), st.clone()); + let (mut then, mut other) = (Vec::new(), Vec::new()); + self.emit_seq(tb, j, ipdom, &mut st_t, &mut then, depth + 1)?; + self.emit_seq(fb, j, ipdom, &mut st_f, &mut other, depth + 1)?; + *st = merge(st_t, st_f); + if then.is_empty() && other.is_empty() { + } else if then.is_empty() { + out.push(Stmt::If { cond: Cond::not(cond), then: other, other: Vec::new() }); + } else { + out.push(Stmt::If { cond, then, other }); + } + self.emitted += 1; + if j == exit { + return Ok(()); + } + b = j; + } + } + } + } + } + } +} + +impl Path { + /// What copying it copies. + fn weight(&self) -> usize { + self.gens.len() + + self + .env + .values() + .map(|v| match v { + Val::Bool(c, d) => 1 + c.size() + d.len(), + _ => 1, + }) + .sum::() + } + + fn grow(&mut self, n: usize) { + if self.gens.len() < n { + self.gens.resize(n, 0); + self.measured.resize(n, false); + self.touched.resize(n, false); + } + } + fn generation(&mut self, r: u32) -> &mut u32 { + self.grow(r as usize + 1); + &mut self.gens[r as usize] + } + fn measured(&mut self, q: u32) -> &mut bool { + self.grow(q as usize + 1); + &mut self.measured[q as usize] + } + fn touched(&mut self, q: u32) -> &mut bool { + self.grow(q as usize + 1); + &mut self.touched[q as usize] + } +} + +/// A gate as statements of the standard library alone, so the program reads +/// in any OpenQASM 3 reader: `ccz` under two controls, and the two-qubit +/// Pauli rotations by their exact basis changes and parity. +pub(super) fn portable(name: &'static str, params: &[f64], q: &[u32], inv: bool) -> Vec { + let g = |n: &'static str, p: Vec, qs: Vec| Stmt::gate(n, p, qs); + match name { + "ccz" => vec![Stmt::Gate { name: "z", params: vec![], controls: vec![q[0], q[1]], qubits: vec![q[2]], inv: false, pow: 1 }], + "rxx" | "ryy" | "rzz" => { + let t = if inv { -params[0] } else { params[0] }; + let (a, b) = (q[0], q[1]); + let (pre, post): (Vec, Vec) = match name { + "rxx" => (vec![g("h", vec![], vec![a]), g("h", vec![], vec![b])], vec![g("h", vec![], vec![a]), g("h", vec![], vec![b])]), + "ryy" => ( + vec![g("sdg", vec![], vec![a]), g("h", vec![], vec![a]), g("sdg", vec![], vec![b]), g("h", vec![], vec![b])], + vec![g("h", vec![], vec![a]), g("s", vec![], vec![a]), g("h", vec![], vec![b]), g("s", vec![], vec![b])], + ), + _ => (vec![], vec![]), + }; + let mut v = pre; + v.push(g("cx", vec![], vec![a, b])); + v.push(g("rz", vec![t], vec![b])); + v.push(g("cx", vec![], vec![a, b])); + v.extend(post); + v + } + _ => vec![Stmt::Gate { name, params: params.to_vec(), controls: Vec::new(), qubits: q.to_vec(), inv, pow: 1 }], + } +} + +/// Two paths' states where they meet: what both hold, and a result measured +/// differently on the two counted as measured again. +fn merge(mut a: Path, mut b: Path) -> Path { + let n = a.gens.len().max(b.gens.len()); + a.grow(n); + b.grow(n); + let env = a.env.into_iter().filter(|(k, v)| b.env.get(k) == Some(v)).collect(); + let gens = a.gens.iter().zip(&b.gens).map(|(&x, &y)| if x == y { x } else { x.max(y) + 1 }).collect(); + let measured = a.measured.iter().zip(&b.measured).map(|(&x, &y)| x || y).collect(); + let touched = a.touched.iter().zip(&b.touched).map(|(&x, &y)| x || y).collect(); + Path { env, gens, measured, touched } +} + +/// Whether `theta` is a dyadic multiple of 2π the bit-exact method takes. +fn is_dyadic(theta: f64) -> bool { + crate::qasm::dyadic(theta).is_some() +} + +/// Reads a QIR module (LLVM text) under its profile. +pub fn read(src: &str) -> Result { + if src.len() > MAX_BYTES { + return Err(err(diag::FORMAT_UNREAD, 0, format!("a module of {} bytes, larger than the {MAX_BYTES} the reader takes", src.len()))); + } + let m = parse_module(src)?; + let mut notes = Vec::new(); + let entries: Vec<&Func> = m.defines.iter().filter(|f| f.attrs.iter().any(|a| a == "entry_point" || m.groups.get(a).is_some_and(|g| g.iter().any(|(n, _)| n == "entry_point")))).collect(); + let f = match entries.as_slice() { + [f] => *f, + [] if m.defines.len() == 1 => { + notes.push(err(diag::FORMAT_INCOMPLETE, m.defines[0].line, format!("@{} has no `entry_point` attribute; read as the entry point, the module's only function", m.defines[0].name))); + &m.defines[0] + } + [] => return Err(err(diag::FORMAT_UNREAD, 0, "no function has the `entry_point` attribute")), + _ => return Err(err(diag::FORMAT_UNREAD, entries[1].line, "more than one entry point")), + }; + let attrs: Vec<(String, Option)> = f.attrs.iter().filter_map(|a| m.groups.get(a)).flatten().cloned().collect(); + let attr = |name: &str| attrs.iter().find(|(n, _)| n == name).and_then(|(_, v)| v.clone()); + // The version. + let major = flag_int(&m, "qir_major_version"); + let minor = flag_int(&m, "qir_minor_version"); + match major { + Some(1 | 2) => {} + Some(v) => return Err(err(diag::FORMAT_VERSION, 0, format!("qir_major_version {v}: the reader takes QIR 1.x and 2.x"))), + None => notes.push(err(diag::FORMAT_INCOMPLETE, 0, "no qir_major_version module flag: read as QIR 2.x")), + } + // The profile. + let profile = match attr("qir_profiles") { + Some(p) => { + let names: Vec<&str> = p.split(',').map(str::trim).collect(); + if names.contains(&"adaptive_profile") { + Profile::Adaptive + } else if names.contains(&"base_profile") { + Profile::Base + } else { + return Err(err(diag::PROFILE_UNKNOWN, f.line, format!("qir_profiles=\"{p}\": the reader takes base_profile and adaptive_profile"))); + } + } + None => { + // The profile the module's instructions need. + let branches = f.blocks.iter().any(|(_, ls)| ls.iter().any(|l| l.text.contains("read_result") || l.text.starts_with("br i1"))); + let p = if branches { Profile::Adaptive } else { Profile::Base }; + notes.push(err(diag::FORMAT_INCOMPLETE, f.line, format!("@{} names no qir_profiles: read under the {} profile, which its instructions need", f.name, p.name()))); + p + } + }; + let allowed = Allowed { + profile, + ints: flag_strings(&m, "int_computations").unwrap_or_default(), + floats: flag_strings(&m, "float_computations").unwrap_or_default(), + ir_functions: flag_bool(&m, "ir_functions").unwrap_or(false), + backwards: flag_int(&m, "backwards_branching").unwrap_or(0), + multi_target: flag_bool(&m, "multiple_target_branching").unwrap_or(false), + multi_return: flag_bool(&m, "multiple_return_points").unwrap_or(false), + dyn_qubits: flag_bool(&m, "dynamic_qubit_management").unwrap_or(false), + dyn_results: flag_bool(&m, "dynamic_result_management").unwrap_or(false), + arrays: flag_bool(&m, "arrays").unwrap_or(false), + }; + if profile == Profile::Base && (allowed.dyn_qubits || allowed.dyn_results) { + return Err(err(diag::PROFILE_FORBIDS, 0, "the base profile forbids dynamic qubit or result management, which the module flags declare")); + } + let count = |name: &str| -> Result, Diagnostic> { + match attr(name) { + None => Ok(None), + Some(v) => v.parse::().ok().filter(|&n| n <= u64::from(crate::qasm::MAX_QUBITS)).map(|n| Some(n as u32)).ok_or_else(|| err(diag::OPERAND, f.line, format!("{name}=\"{v}\": not a count the reader takes (at most {})", crate::qasm::MAX_QUBITS))), + } + }; + let (nq, nr) = (count("required_num_qubits")?, count("required_num_results")?); + for (name, v) in [("required_num_qubits", nq), ("required_num_results", nr)] { + if v.is_none() && !(allowed.dyn_qubits || allowed.dyn_results) { + notes.push(err(diag::FORMAT_INCOMPLETE, f.line, format!("@{} gives no {name}: the count is the highest index used, plus one", f.name))); + } + } + if f.blocks.is_empty() { + return Err(err(diag::FORMAT_UNREAD, f.line, format!("@{} has no blocks", f.name))); + } + let labels: BTreeMap<&str, usize> = f.blocks.iter().enumerate().map(|(i, (l, _))| (l.as_str(), i)).collect(); + + let mut r = Reader { + m: &m, + f, + allowed: allowed.clone(), + n_qubits: nq.unwrap_or(0), + n_results: nr.unwrap_or(0), + declared_qubits: nq.is_some(), + declared_results: nr.is_some(), + labels, + notes: Vec::new(), + records: Vec::new(), + arbitrary: 0, + emitted: 0, + visits: 0, + instructions: 0, + copied: 0, + used_qubits: 0, + used_results: 0, + }; + + // The control-flow graph: forward only, unless loops are declared (and + // then this reader does not take them). + let exit = f.blocks.len(); + let mut succ: Vec> = Vec::with_capacity(exit); + let mut rets = 0; + for b in 0..exit { + let (t, line) = r.terminator(b)?; + succ.push(match t { + Term::Ret => { + rets += 1; + vec![exit] + } + Term::Br(l) => vec![r.block(l, line)?], + Term::CondBr(_, a, c) => vec![r.block(a, line)?, r.block(c, line)?], + }); + } + // Depth-first from the entry block: a back edge is a loop. + let mut state = vec![0u8; exit]; + let mut order = Vec::new(); + let mut stack: Vec<(usize, usize)> = vec![(0, 0)]; + state[0] = 1; + while let Some(&mut (n, ref mut k)) = stack.last_mut() { + if *k < succ[n].len() { + let s = succ[n][*k]; + *k += 1; + if s == exit { + continue; + } + match state[s] { + 0 => { + state[s] = 1; + stack.push((s, 0)); + } + 1 => { + let line = f.blocks[n].1.last().map_or(f.line, |l| l.no); + return Err(if allowed.backwards > 0 && profile == Profile::Adaptive { r.not_taken(line, "a loop (backwards branching)") } else { r.forbidden(line, "a loop (its module flags do not declare backwards branching)") }); + } + _ => {} + } + } else { + state[n] = 2; + order.push(n); + stack.pop(); + } + } + let reachable_rets = order.iter().filter(|&&n| succ[n].contains(&exit)).count(); + if reachable_rets > 1 && !(allowed.multi_return && profile == Profile::Adaptive) { + let line = order.iter().filter(|&&n| succ[n].contains(&exit)).map(|&n| f.blocks[n].1.last().map_or(f.line, |l| l.no)).max().unwrap_or(f.line); + return Err(r.forbidden(line, "more than one return (its module flags do not declare multiple return points)")); + } + let _ = rets; + // Immediate post-dominators, in reverse topological order (`order` is a post-order). + let mut ipdom = vec![usize::MAX; exit + 1]; + let mut depth = vec![0usize; exit + 1]; + ipdom[exit] = exit; + for &n in &order { + let lca = |mut a: usize, mut b: usize, ipdom: &[usize], depth: &[usize]| { + while a != b { + if depth[a] >= depth[b] && a != exit { + a = ipdom[a]; + } else { + b = ipdom[b]; + } + } + a + }; + let mut x = succ[n][0]; + for &s in &succ[n][1..] { + x = lca(x, s, &ipdom, &depth); + } + ipdom[n] = x; + depth[n] = depth[x] + 1; + } + + let mut st = Path::default(); + st.grow(r.n_qubits.max(r.n_results) as usize); + let mut body = Vec::new(); + r.emit_seq(0, exit, &ipdom, &mut st, &mut body, 0)?; + + let n_qubits = if r.declared_qubits { r.n_qubits } else { r.used_qubits }; + let n_results = if r.declared_results { r.n_results } else { r.used_results }; + if n_qubits == 0 { + return Err(err(diag::NO_QUBITS, f.line, format!("@{} uses no qubits", f.name))); + } + if r.arbitrary > 0 { + r.notes.push(err(diag::ARBITRARY_ANGLE, 0, format!("{} rotation{} at angles that are not dyadic multiples of 2π: the double-precision methods take them; the bit-exact method refuses them (Q500)", r.arbitrary, if r.arbitrary == 1 { "" } else { "s" }))); + } + notes.extend(std::mem::take(&mut r.notes)); + let circuit = Circuit { n_qubits, n_bits: n_results, body }; + let version = match (major, minor) { + (Some(a), Some(b)) => format!("{a}.{b}"), + (Some(a), None) => format!("{a}"), + _ => "unstated".into(), + }; + let qasm = circuit.to_qasm(&format!("Read from QIR {version}, {} profile, entry point @{}: qubit i is q[i], result r is c[r].", profile.name(), f.name)); + let mut caps = Vec::new(); + if !allowed.ints.is_empty() { + caps.push(json!({ "int_computations": allowed.ints })); + } + if !allowed.floats.is_empty() { + caps.push(json!({ "float_computations": allowed.floats })); + } + for (name, on) in [("ir_functions", allowed.ir_functions), ("multiple_target_branching", allowed.multi_target), ("multiple_return_points", allowed.multi_return), ("dynamic_qubit_management", allowed.dyn_qubits), ("dynamic_result_management", allowed.dyn_results), ("arrays", allowed.arrays)] { + if on { + caps.push(json!(name)); + } + } + if allowed.backwards > 0 { + caps.push(json!({ "backwards_branching": allowed.backwards })); + } + let summary = json!({ + "format": "qir", + "version": version, + "profile": profile.name(), + "entry_point": f.name, + "qubits": n_qubits, + "results": n_results, + "capabilities": caps, + "recorded": r.records, + "typed_pointers": m.typed_pointers, + "arbitrary_angles": r.arbitrary, + }); + Ok(Reading { qasm, notes, summary }) +} + +// --------------------------------------------------------------------------- +// Writing +// --------------------------------------------------------------------------- + +/// The intrinsic calls a program's op is written as: `(intrinsic, its +/// angles, its qubits)`. Exact, up to a global phase where the op has no +/// intrinsic of its own. +fn lower(kind: Kind, q: &[u32], p: &[f64]) -> Vec<(&'static str, Vec, Vec)> { + use std::f64::consts::FRAC_PI_4; + let one = |n: &'static str, a: u32| (n, vec![], vec![a]); + let rot = |n: &'static str, t: f64, a: u32| (n, vec![t], vec![a]); + let cx = |c: u32, t: u32| ("cx", vec![], vec![c, t]); + match kind { + Kind::Id => vec![], + Kind::X => vec![one("x", q[0])], + Kind::Y => vec![one("y", q[0])], + Kind::Z => vec![one("z", q[0])], + Kind::H => vec![one("h", q[0])], + Kind::S => vec![one("s", q[0])], + Kind::Sdg => vec![one("s__adj", q[0])], + Kind::T => vec![one("t", q[0])], + Kind::Tdg => vec![one("t__adj", q[0])], + Kind::Sx => vec![one("h", q[0]), one("s", q[0]), one("h", q[0])], + Kind::Sxdg => vec![one("h", q[0]), one("s__adj", q[0]), one("h", q[0])], + Kind::Rx => vec![rot("rx", p[0], q[0])], + Kind::Ry => vec![rot("ry", p[0], q[0])], + Kind::Rz => vec![rot("rz", p[0], q[0])], + Kind::P => { + // A phase at a multiple of π/4 is written in Clifford+T gates, so a + // Clifford+T program stays one. + let k = (p[0] / FRAC_PI_4).round(); + if (k * FRAC_PI_4).to_bits() == p[0].to_bits() || p[0] == 0.0 { + let k = (k as i64).rem_euclid(8); + let mut v = Vec::new(); + if k & 4 != 0 { + v.push(one("z", q[0])); + } + if k & 2 != 0 { + v.push(one("s", q[0])); + } + if k & 1 != 0 { + v.push(one("t", q[0])); + } + v + } else { + vec![rot("rz", p[0], q[0])] + } + } + Kind::U => vec![rot("rz", p[2], q[0]), rot("ry", p[0], q[0]), rot("rz", p[1], q[0])], + Kind::Cx => vec![cx(q[0], q[1])], + Kind::Cy => vec![one("s__adj", q[1]), cx(q[0], q[1]), one("s", q[1])], + Kind::Cz => vec![("cz", vec![], vec![q[0], q[1]])], + Kind::Ch => vec![one("h", q[1]), one("s__adj", q[1]), cx(q[0], q[1]), one("h", q[1]), one("t", q[1]), cx(q[0], q[1]), one("t", q[1]), one("h", q[1]), one("s", q[1]), one("x", q[1]), one("s", q[0])], + Kind::Cp => { + let mut v = lower(Kind::P, &[q[0]], &[p[0] / 2.0]); + v.push(cx(q[0], q[1])); + v.extend(lower(Kind::P, &[q[1]], &[-p[0] / 2.0])); + v.push(cx(q[0], q[1])); + v.extend(lower(Kind::P, &[q[1]], &[p[0] / 2.0])); + v + } + Kind::Crz => vec![rot("rz", p[0] / 2.0, q[1]), cx(q[0], q[1]), rot("rz", -p[0] / 2.0, q[1]), cx(q[0], q[1])], + Kind::Cry => vec![rot("ry", p[0] / 2.0, q[1]), cx(q[0], q[1]), rot("ry", -p[0] / 2.0, q[1]), cx(q[0], q[1])], + Kind::Crx => { + let mut v = vec![one("h", q[1])]; + v.extend(lower(Kind::Crz, q, p)); + v.push(one("h", q[1])); + v + } + Kind::Cu => { + // cu(θ, φ, λ, γ) = controlled e^{iγ} U(θ, φ, λ), as the standard library writes it. + let (t, ph, l, g) = (p[0], p[1], p[2], p[3]); + let mut v = lower(Kind::P, &[q[0]], &[g]); + v.extend(lower(Kind::P, &[q[0]], &[(l + ph) / 2.0])); + v.extend(lower(Kind::P, &[q[1]], &[(l - ph) / 2.0])); + v.push(cx(q[0], q[1])); + v.extend(lower(Kind::U, &[q[1]], &[-t / 2.0, 0.0, -(ph + l) / 2.0])); + v.push(cx(q[0], q[1])); + v.extend(lower(Kind::U, &[q[1]], &[t / 2.0, ph, 0.0])); + v + } + Kind::Swap => vec![("swap", vec![], vec![q[0], q[1]])], + Kind::Iswap => vec![one("s", q[0]), one("s", q[1]), one("h", q[0]), cx(q[0], q[1]), cx(q[1], q[0]), one("h", q[1])], + Kind::Rxx => vec![("rxx", vec![p[0]], vec![q[0], q[1]])], + Kind::Ryy => vec![("ryy", vec![p[0]], vec![q[0], q[1]])], + Kind::Rzz => vec![("rzz", vec![p[0]], vec![q[0], q[1]])], + Kind::Ccx => vec![("ccx", vec![], vec![q[0], q[1], q[2]])], + Kind::Ccz => vec![one("h", q[2]), ("ccx", vec![], vec![q[0], q[1], q[2]]), one("h", q[2])], + Kind::Cswap => vec![cx(q[2], q[1]), ("ccx", vec![], vec![q[0], q[1], q[2]]), cx(q[2], q[1])], + } +} + +fn ptr(i: u32) -> String { + if i == 0 { "ptr null".to_string() } else { format!("ptr inttoptr (i64 {i} to ptr)") } +} + +fn hexf(v: f64) -> String { + format!("0x{:016X}", v.to_bits()) +} + +/// The intrinsic's full name: `s__adj` is `__quantum__qis__s__adj`, `h` is `__quantum__qis__h__body`. +fn intrinsic(n: &str) -> String { + if n.ends_with("__adj") { format!("__quantum__qis__{n}") } else { format!("__quantum__qis__{n}__body") } +} + +struct Writer { + lines: Vec, + declared: Vec<(String, String)>, +} + +impl Writer { + fn declare(&mut self, name: String, sig: &str) { + if !self.declared.iter().any(|(n, _)| *n == name) { + self.declared.push((name, sig.to_string())); + } + } + fn gate(&mut self, name: &'static str, params: &[f64], qubits: &[u32]) { + let full = intrinsic(name); + let sig: Vec<&str> = params.iter().map(|_| "double").chain(qubits.iter().map(|_| "ptr")).collect(); + self.declare(full.clone(), &format!("void @{full}({})", sig.join(", "))); + let args: Vec = params.iter().map(|&p| format!("double {}", hexf(p))).chain(qubits.iter().map(|&q| ptr(q))).collect(); + self.lines.push(format!(" call void @{full}({})", args.join(", "))); + } + fn op(&mut self, p: &Program, i: usize) { + let op = &p.ops[i]; + for (n, a, q) in lower(op.kind, &op.qubits, &op.params) { + self.gate(n, &a, &q); + } + } + fn measure(&mut self, q: u32, r: u32) { + self.declare("__quantum__qis__mz__body".into(), "void @__quantum__qis__mz__body(ptr, ptr writeonly) #1"); + self.lines.push(format!(" call void @__quantum__qis__mz__body({}, {})", ptr(q), ptr(r).replacen("ptr", "ptr writeonly", 1))); + } +} + +/// Writes `p` as QIR 2.0 under `profile`: the base profile when none is +/// given and the program's measurements all come at the end, else the +/// adaptive profile. A program the base profile cannot hold is refused when +/// that profile is asked for. What QIR has no place for (timing, +/// calibrations, outputs) is listed in the notes. +pub fn write(p: &Program, profile: Option) -> Result<(String, Vec), Diagnostic> { + let mut notes = Vec::new(); + dropped(p, &mut notes); + let profile = match (profile, &p.dynamic) { + (Some(Profile::Base), Some(_)) => return Err(Diagnostic::new(diag::WRITE_CANNOT, None, "this program measures in the middle, resets a qubit after a gate, or branches on measured bits: the base profile cannot hold it; write the adaptive profile")), + (Some(pr), _) => pr, + (None, None) => Profile::Base, + (None, Some(_)) => Profile::Adaptive, + }; + let mut w = Writer { lines: Vec::new(), declared: Vec::new() }; + w.declare("__quantum__rt__initialize".into(), "void @__quantum__rt__initialize(ptr)"); + let mut body = vec!["entry:".to_string(), " call void @__quantum__rt__initialize(ptr null)".to_string()]; + let n_results; + match &p.dynamic { + None => { + body.push(" br label %body".into()); + body.push("body:".into()); + for i in 0..p.ops.len() { + w.op(p, i); + } + body.append(&mut w.lines); + body.push(" br label %measurements".into()); + body.push("measurements:".into()); + for &(q, b) in &p.measured { + w.measure(q, b); + } + body.append(&mut w.lines); + body.push(" br label %output".into()); + n_results = p.n_bits; + } + Some(steps) => { + let mut bits = p.n_bits; + for s in steps { + match s { + Instr::Measure { bit, .. } => bits = bits.max(bit + 1), + Instr::JumpUnless(c, _) => bits = bits.max(c.bits.iter().max().map_or(0, |b| b + 1)), + _ => {} + } + } + n_results = bits; + let end = steps.len(); + let mut target = vec![false; end + 1]; + for (i, s) in steps.iter().enumerate() { + match s { + Instr::Jump(t) | Instr::JumpUnless(_, t) => { + if *t <= i { + return Err(Diagnostic::new(diag::WRITE_CANNOT, None, "this program's steps jump backwards (a loop): QIR's adaptive profile is written here with forward branches only")); + } + target[(*t).min(end)] = true; + target[i + 1] = true; + } + _ => {} + } + } + let label = |i: usize| if i >= end { "output".to_string() } else { format!("s{i}") }; + body.push(" br label %s0".into()); + body.push("s0:".into()); + let mut chain = 0usize; + let mut reads = 0usize; + for (i, s) in steps.iter().enumerate() { + if i > 0 && target[i] { + if !body.last().is_some_and(|l| l.starts_with(" br ")) { + body.append(&mut w.lines); + body.push(format!(" br label %{}", label(i))); + } + body.append(&mut w.lines); + body.push(format!("{}:", label(i))); + } + match s { + Instr::Gate(k) => w.op(p, *k), + Instr::Measure { qubit, bit } => w.measure(*qubit, *bit), + Instr::Reset(q) => { + w.declare("__quantum__qis__reset__body".into(), "void @__quantum__qis__reset__body(ptr)"); + w.lines.push(format!(" call void @__quantum__qis__reset__body({})", ptr(*q))); + } + Instr::Jump(t) => { + body.append(&mut w.lines); + body.push(format!(" br label %{}", label(*t))); + } + Instr::JumpUnless(c, t) => { + // Read each bit and branch on it: the condition holds when every bit + // matches its place in `value` (or, for `!=`, when one does not). + w.declare("__quantum__rt__read_result".into(), "i1 @__quantum__rt__read_result(ptr readonly)"); + let (all_match, mismatch) = if c.equal { (label(i + 1), label(*t)) } else { (label(*t), label(i + 1)) }; + body.append(&mut w.lines); + for (k, &b) in c.bits.iter().enumerate() { + if k > 0 { + body.push(format!("c{chain}_{k}:")); + } + let next = if k + 1 == c.bits.len() { all_match.clone() } else { format!("c{chain}_{}", k + 1) }; + body.push(format!(" %r{reads} = call i1 @__quantum__rt__read_result({})", ptr(b).replacen("ptr", "ptr readonly", 1))); + if c.value >> k & 1 == 1 { + body.push(format!(" br i1 %r{reads}, label %{next}, label %{mismatch}")); + } else { + body.push(format!(" br i1 %r{reads}, label %{mismatch}, label %{next}")); + } + reads += 1; + } + if c.bits.is_empty() { + let to = if c.value == 0 { all_match } else { mismatch }; + body.push(format!(" br label %{to}")); + } + chain += 1; + } + } + } + body.append(&mut w.lines); + if !body.last().is_some_and(|l| l.starts_with(" br ")) { + body.push(" br label %output".into()); + } + } + } + body.push("output:".into()); + w.declare("__quantum__rt__array_record_output".into(), "void @__quantum__rt__array_record_output(i64, ptr)"); + w.declare("__quantum__rt__result_record_output".into(), "void @__quantum__rt__result_record_output(ptr, ptr)"); + let mut globals = vec![label_global(0, "c")]; + body.push(format!(" call void @__quantum__rt__array_record_output(i64 {}, ptr @0)", p.n_bits)); + for b in 0..p.n_bits { + globals.push(label_global(b as usize + 1, &format!("c{b}"))); + body.push(format!(" call void @__quantum__rt__result_record_output({}, ptr @{})", ptr(b), b + 1)); + } + body.push(" ret i64 0".into()); + + let mut s = String::new(); + s.push_str("; QIR 2.0 written by wai-quantum: the same bytes on every platform.\n"); + s.push_str("source_filename = \"wai_quantum\"\n\n"); + s.push_str("%Qubit = type opaque\n%Result = type opaque\n\n"); + for g in &globals { + s.push_str(g); + s.push('\n'); + } + s.push_str("\ndefine i64 @main() #0 {\n"); + for l in &body { + s.push_str(l); + s.push('\n'); + } + s.push_str("}\n\n"); + for (_, sig) in &w.declared { + s.push_str(&format!("declare {sig}\n")); + } + s.push_str(&format!( + "\nattributes #0 = {{ \"entry_point\" \"output_labeling_schema\"=\"wai_quantum\" \"qir_profiles\"=\"{}\" \"required_num_qubits\"=\"{}\" \"required_num_results\"=\"{}\" }}\n", + profile.attribute(), + p.n_qubits, + n_results + )); + s.push_str("attributes #1 = { \"irreversible\" }\n\n"); + match profile { + Profile::Base => { + s.push_str("!llvm.module.flags = !{!0, !1, !2, !3}\n\n"); + } + Profile::Adaptive => { + s.push_str("!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}\n\n"); + } + } + s.push_str("!0 = !{i32 1, !\"qir_major_version\", i32 2}\n"); + s.push_str("!1 = !{i32 7, !\"qir_minor_version\", i32 0}\n"); + s.push_str("!2 = !{i32 1, !\"dynamic_qubit_management\", i1 false}\n"); + s.push_str("!3 = !{i32 1, !\"dynamic_result_management\", i1 false}\n"); + if profile == Profile::Adaptive { + s.push_str("!4 = !{i32 1, !\"ir_functions\", i1 false}\n"); + s.push_str("!5 = !{i32 1, !\"backwards_branching\", i2 0}\n"); + s.push_str("!6 = !{i32 1, !\"multiple_target_branching\", i1 false}\n"); + s.push_str("!7 = !{i32 1, !\"multiple_return_points\", i1 false}\n"); + } + Ok((s, notes)) +} + +/// `@n = internal constant [k x i8] c"text\00"`. +fn label_global(n: usize, text: &str) -> String { + format!("@{n} = internal constant [{} x i8] c\"{text}\\00\"", text.len() + 1) +} + +/// What a program carries that QIR and jeff have no place for. +pub(super) fn dropped(p: &Program, notes: &mut Vec) { + let x = &p.extra; + if !x.timing.is_empty() { + notes.push(Diagnostic::new(diag::WRITE_DROPPED, None, format!("{} timing statement{} (delays, boxes, barriers, gate durations) left out: the format has no place for them, and they change nothing a method computes", x.timing.len(), if x.timing.len() == 1 { "" } else { "s" }))); + } + if !x.calibrations.is_empty() { + notes.push(Diagnostic::new(diag::WRITE_DROPPED, None, format!("{} calibration statement{} left out", x.calibrations.len(), if x.calibrations.len() == 1 { "" } else { "s" }))); + } + if !x.outputs.is_empty() { + notes.push(Diagnostic::new(diag::WRITE_DROPPED, None, format!("{} `output` declaration{} left out: the bits are written as the program's results", x.outputs.len(), if x.outputs.len() == 1 { "" } else { "s" }))); + } +} diff --git a/wai/wai-quantum/src/engine/formats/tests.rs b/wai/wai-quantum/src/engine/formats/tests.rs new file mode 100644 index 000000000..dfbc29422 --- /dev/null +++ b/wai/wai-quantum/src/engine/formats/tests.rs @@ -0,0 +1,851 @@ +//! Ground truth first: small programs written by hand in each format, with +//! the unitary or the outcomes they must have, and every refusal with its +//! code and its place. + +use super::capnp::{Out, Ptr}; +use super::*; +use crate::engine::equiv; +use crate::qasm; +use serde_json::json; + +fn qasm_of(format: Format, bytes: &[u8]) -> String { + read(format, bytes).unwrap_or_else(|d| panic!("{} does not read: {d}", format.name())).qasm +} + +/// The ladder's verdict on two programs' texts, and how it decided. +fn verdict(a: &str, b: &str) -> (String, String) { + let r = equiv::equivalent(a, b); + (r["verdict"].as_str().unwrap().to_string(), r["how"].as_str().unwrap().to_string()) +} + +fn assert_equivalent(got: &str, want: &str) { + let (v, how) = verdict(got, want); + assert_eq!(v, "equivalent", "not the reference ({how}):\n--- read\n{got}\n--- reference\n{want}"); +} + +/// The counts a program gives over `shots` shots. +fn counts(text: &str, shots: u64) -> serde_json::Map { + let req = json!({ "qasm": text, "shots": shots, "seed": 7 }); + let plan = crate::engine::catalog::plan(&req, 1 << 30).unwrap_or_else(|e| panic!("{e}\n{text}")); + let out = crate::engine::run::run(&req, &plan).unwrap_or_else(|e| panic!("{e}\n{text}")); + out["counts"].as_object().cloned().unwrap_or_else(|| panic!("no counts in {out}")) +} + +fn code(d: &Diagnostic) -> &'static str { + d.code +} + +// --------------------------------------------------------------------------- +// QIR, read +// --------------------------------------------------------------------------- + +const BELL_BASE: &str = r#" +@0 = internal constant [3 x i8] c"r0\00" +@1 = internal constant [3 x i8] c"r1\00" + +define i64 @Bell() #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body + +body: ; preds = %entry + call void @__quantum__qis__h__body(ptr null) + call void @__quantum__qis__cnot__body(ptr null, ptr inttoptr (i64 1 to ptr)) + br label %measurements + +measurements: ; preds = %body + call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + call void @__quantum__qis__mz__body(ptr inttoptr (i64 1 to ptr), ptr writeonly inttoptr (i64 1 to ptr)) + br label %output + +output: ; preds = %measurements + call void @__quantum__rt__result_record_output(ptr null, ptr @0) + call void @__quantum__rt__result_record_output(ptr inttoptr (i64 1 to ptr), ptr @1) + ret i64 0 +} + +declare void @__quantum__qis__h__body(ptr) +declare void @__quantum__qis__cnot__body(ptr, ptr) +declare void @__quantum__qis__mz__body(ptr, ptr writeonly) #1 +declare void @__quantum__rt__initialize(ptr) +declare void @__quantum__rt__result_record_output(ptr, ptr) + +attributes #0 = { "entry_point" "qir_profiles"="base_profile" "output_labeling_schema"="schema_id" "required_num_qubits"="2" "required_num_results"="2" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +"#; + +/// A base-profile module with `body` as its gate block and `meas` as its +/// measurements, `flags` added to its module flags. +fn base(nq: u32, nr: u32, body: &str, meas: &str) -> String { + format!( + "define i64 @main() #0 {{\nentry:\n call void @__quantum__rt__initialize(ptr null)\n br label %body\nbody:\n{body}\n br label %m\nm:\n{meas}\n ret i64 0\n}}\n\nattributes #0 = {{ \"entry_point\" \"qir_profiles\"=\"base_profile\" \"output_labeling_schema\"=\"x\" \"required_num_qubits\"=\"{nq}\" \"required_num_results\"=\"{nr}\" }}\nattributes #1 = {{ \"irreversible\" }}\n!llvm.module.flags = !{{!0, !1, !2, !3}}\n!0 = !{{i32 1, !\"qir_major_version\", i32 2}}\n!1 = !{{i32 7, !\"qir_minor_version\", i32 1}}\n!2 = !{{i32 1, !\"dynamic_qubit_management\", i1 false}}\n!3 = !{{i32 1, !\"dynamic_result_management\", i1 false}}\n" + ) +} + +/// An adaptive-profile module with one block of instructions, `extra` +/// module flags after the mandatory four. +fn adaptive(nq: u32, nr: u32, blocks: &str, extra: &[&str]) -> String { + let mut flags = String::new(); + let mut refs = vec!["!0".to_string(), "!1".into(), "!2".into(), "!3".into()]; + for (i, f) in extra.iter().enumerate() { + refs.push(format!("!{}", 4 + i)); + flags.push_str(&format!("!{} = {f}\n", 4 + i)); + } + format!( + "define i64 @main() #0 {{\nentry:\n call void @__quantum__rt__initialize(ptr null)\n{blocks}\n}}\n\nattributes #0 = {{ \"entry_point\" \"qir_profiles\"=\"adaptive_profile\" \"output_labeling_schema\"=\"x\" \"required_num_qubits\"=\"{nq}\" \"required_num_results\"=\"{nr}\" }}\n!llvm.module.flags = !{{{}}}\n!0 = !{{i32 1, !\"qir_major_version\", i32 2}}\n!1 = !{{i32 7, !\"qir_minor_version\", i32 1}}\n!2 = !{{i32 1, !\"dynamic_qubit_management\", i1 false}}\n!3 = !{{i32 1, !\"dynamic_result_management\", i1 false}}\n{flags}", + refs.join(", ") + ) +} + +fn q(i: u32) -> String { + if i == 0 { "ptr null".into() } else { format!("ptr inttoptr (i64 {i} to ptr)") } +} + +#[test] +fn a_base_profile_bell_module_reads_to_the_bell_circuit() { + let r = read(Format::Qir, BELL_BASE.as_bytes()).unwrap(); + assert!(r.notes.is_empty(), "{:?}", r.notes); + assert_eq!((r.summary["profile"].as_str(), r.summary["version"].as_str(), r.summary["entry_point"].as_str()), (Some("base"), Some("2.0"), Some("Bell"))); + assert_eq!(r.summary["recorded"], json!([0, 1])); + assert_eq!(verdict(&r.qasm, "OPENQASM 3.0; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;"), ("equivalent".into(), "tableau".into())); + let c = counts(&r.qasm, 400); + assert_eq!(c.keys().cloned().collect::>(), vec!["00".to_string(), "11".to_string()], "{c:?}"); +} + +/// Every intrinsic the reader takes, each written once at an angle that is +/// not dyadic where it takes one, against the OpenQASM gates it means. +#[test] +fn every_intrinsic_reads_as_the_gate_it_names() { + let (a, b) = (0.3f64, -1.1f64); + let calls = [ + format!("call void @__quantum__qis__h__body({})", q(0)), + format!("call void @__quantum__qis__h__body({})", q(1)), + format!("call void @__quantum__qis__h__body({})", q(2)), + format!("call void @__quantum__qis__x__body({})", q(0)), + format!("call void @__quantum__qis__y__body({})", q(1)), + format!("call void @__quantum__qis__z__body({})", q(2)), + format!("call void @__quantum__qis__s__body({})", q(0)), + format!("call void @__quantum__qis__s__adj({})", q(1)), + format!("call void @__quantum__qis__t__body({})", q(2)), + format!("call void @__quantum__qis__t__adj({})", q(0)), + format!("call void @__quantum__qis__sx__body({})", q(1)), + format!("call void @__quantum__qis__rx__body(double {a:e}, {})", q(0)), + format!("call void @__quantum__qis__ry__body(double 0x{:016X}, {})", b.to_bits(), q(1)), + format!("call void @__quantum__qis__rz__body(double 2.5, {})", q(2)), + format!("call void @__quantum__qis__rz__adj(double 0.7, {})", q(0)), + format!("call void @__quantum__qis__r1__body(double 0.9, {})", q(1)), + format!("call void @__quantum__qis__cnot__body({}, {})", q(0), q(1)), + format!("call void @__quantum__qis__cx__body({}, {})", q(1), q(2)), + format!("call void @__quantum__qis__cy__body({}, {})", q(2), q(0)), + format!("call void @__quantum__qis__cz__body({}, {})", q(0), q(2)), + format!("call void @__quantum__qis__swap__body({}, {})", q(1), q(2)), + format!("call void @__quantum__qis__ccx__body({}, {}, {})", q(0), q(1), q(2)), + format!("call void @__quantum__qis__ccz__body({}, {}, {})", q(2), q(1), q(0)), + format!("call void @__quantum__qis__rxx__body(double 0.4, {}, {})", q(0), q(1)), + format!("call void @__quantum__qis__ryy__body(double -0.6, {}, {})", q(1), q(2)), + format!("call void @__quantum__qis__rzz__body(double 1.3, {}, {})", q(2), q(0)), + ]; + let body: String = calls.iter().map(|c| format!(" {c}\n")).collect(); + let module = base(3, 0, &body, ""); + let r = read(Format::Qir, module.as_bytes()).unwrap(); + let want = format!( + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[0]; h q[1]; h q[2];\nx q[0]; y q[1]; z q[2]; s q[0]; sdg q[1]; t q[2]; tdg q[0]; sx q[1];\nrx({a}) q[0]; ry({b}) q[1]; rz(2.5) q[2]; rz(-0.7) q[0]; p(0.9) q[1];\ncx q[0], q[1]; cx q[1], q[2]; cy q[2], q[0]; cz q[0], q[2]; swap q[1], q[2]; ccx q[0], q[1], q[2]; ctrl(2) @ z q[2], q[1], q[0];\nrxx(0.4) q[0], q[1]; ryy(-0.6) q[1], q[2]; rzz(1.3) q[2], q[0];\n" + ); + assert_eq!(verdict(&r.qasm, &want), ("equivalent".into(), "exact".into()), "{}", r.qasm); + // The text it gives reads in any reader of the standard library: no gate outside it. + for w in ["rxx", "ryy", "rzz", "ccz"] { + assert!(!r.qasm.contains(&format!("{w}(")) && !r.qasm.contains(&format!("{w} ")), "{w} in\n{}", r.qasm); + } + // Rotations at arbitrary angles read, and say so. + assert!(r.notes.iter().any(|n| n.code == diag::ARBITRARY_ANGLE), "{:?}", r.notes); +} + +/// The module this crate wrote before QIR 2 (typed pointers, QIR 1.0) reads +/// to the circuit it was written from, for every algorithm of the catalog. +// It writes QIR 1 with the dyadic writer, which `quantum_qir` holds. +#[cfg(feature = "quantum_qir")] +#[test] +fn qir_one_modules_with_typed_pointers_read() { + use crate::quantum_toolchain as qt; + let mut n = 0; + for (id, _, _) in qt::catalog() { + let Some(c) = qt::build_algorithm(id, 3, 5) else { continue }; + let ir = crate::quantum_qir::to_qir(&c, true); + let r = read(Format::Qir, ir.as_bytes()).unwrap_or_else(|d| panic!("{id}: {d}")); + assert_eq!(r.summary["typed_pointers"], json!(true)); + assert_eq!(r.summary["version"], json!("1.0")); + assert_equivalent(&r.qasm, &qt::to_qasm(&c)); + n += 1; + } + assert!(n >= 8, "{n}"); +} + +/// The adaptive profile's own example, a teleport chain: four measurements +/// in the middle, each followed by a correction on the bit it measured. A +/// state teleported along the chain arrives: with the source flipped to |1⟩ +/// (and the chain's other halves undone), the last qubit always reads 1. +#[test] +fn the_adaptive_teleport_chain_reads_and_teleports() { + let module = r#" +define i64 @TeleportChain() local_unnamed_addr #0 { +entry: + call void @__quantum__rt__initialize(ptr null) + br label %body + +body: + tail call void @__quantum__qis__x__body(ptr null) + tail call void @__quantum__qis__h__body(ptr nonnull inttoptr (i64 1 to ptr)) + tail call void @__quantum__qis__cnot__body(ptr nonnull inttoptr (i64 1 to ptr), ptr nonnull inttoptr (i64 2 to ptr)) + tail call void @__quantum__qis__cnot__body(ptr null, ptr nonnull inttoptr (i64 1 to ptr)) + tail call void @__quantum__qis__h__body(ptr null) + tail call void @__quantum__qis__mz__body(ptr null, ptr writeonly null) + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 1 to ptr), ptr writeonly nonnull inttoptr (i64 1 to ptr)) + %0 = tail call i1 @__quantum__rt__read_result(ptr readonly nonnull inttoptr (i64 1 to ptr)) + br i1 %0, label %then__1, label %continue__1 + +then__1: + tail call void @__quantum__qis__x__body(ptr nonnull inttoptr (i64 2 to ptr)) + br label %continue__1 + +continue__1: + %1 = tail call i1 @__quantum__rt__read_result(ptr readonly null) + br i1 %1, label %then__2, label %continue__2 + +then__2: + tail call void @__quantum__qis__z__body(ptr nonnull inttoptr (i64 2 to ptr)) + br label %continue__2 + +continue__2: + tail call void @__quantum__qis__mz__body(ptr nonnull inttoptr (i64 2 to ptr), ptr writeonly nonnull inttoptr (i64 2 to ptr)) + br label %exit + +exit: + call void @__quantum__rt__result_record_output(ptr nonnull inttoptr (i64 2 to ptr), ptr @0) + ret i64 0 +} + +attributes #0 = { "entry_point" "qir_profiles"="adaptive_profile" "output_labeling_schema"="schema_id" "required_num_qubits"="3" "required_num_results"="3" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9} +!0 = !{i32 1, !"qir_major_version", i32 2} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +!4 = !{i32 5, !"int_computations", !10} +!5 = !{i32 5, !"float_computations", !11} +!6 = !{i32 1, !"ir_functions", i1 false} +!7 = !{i32 1, !"backwards_branching", i2 0} +!8 = !{i32 1, !"multiple_target_branching", i1 false} +!9 = !{i32 1, !"multiple_return_points", i1 false} +!10 = !{!"i32", !"i64"} +!11 = !{!"float", !"double"} +"#; + let r = read(Format::Qir, module.as_bytes()).unwrap(); + assert_eq!(r.summary["profile"], json!("adaptive")); + let p = qasm::parse(&r.qasm).unwrap(); + assert!(p.dynamic.is_some(), "{}", r.qasm); + // c[2] (the teleported qubit) is always 1, whatever the two corrections measured. + let c = counts(&r.qasm, 300); + assert_eq!(c.len(), 4, "{c:?}"); + for k in c.keys() { + assert!(k.starts_with('1'), "the teleported bit is c[2], first: {k} in {c:?}"); + } +} + +/// Classical computation the flags declare is computed on reading where its +/// operands are constants; a condition built from results with `i1` logic +/// is a condition on the bits. +#[test] +fn declared_computation_and_boolean_logic_read() { + let blocks = format!( + " %a = fadd double 0.25, 0.5\n %b = fmul double %a, 2.0\n call void @__quantum__qis__ry__body(double %b, {q0})\n call void @__quantum__qis__h__body({q1})\n call void @__quantum__qis__mz__body({q0}, ptr writeonly null)\n call void @__quantum__qis__mz__body({q1}, ptr writeonly inttoptr (i64 1 to ptr))\n %r0 = call i1 @__quantum__rt__read_result(ptr readonly null)\n %r1 = call i1 @__quantum__rt__read_result(ptr readonly inttoptr (i64 1 to ptr))\n %x = xor i1 %r0, %r1\n %n = xor i1 %x, true\n br i1 %n, label %same, label %out\nsame:\n call void @__quantum__qis__x__body({q2})\n br label %out\nout:\n call void @__quantum__qis__mz__body({q2}, ptr writeonly inttoptr (i64 2 to ptr))\n ret i64 0", + q0 = q(0), + q1 = q(1), + q2 = q(2) + ); + let flags = ["!{i32 5, !\"float_computations\", !10}", "!{i32 1, !\"backwards_branching\", i2 0}"]; + let mut module = adaptive(3, 3, &blocks, &flags); + module.push_str("!10 = !{!\"double\"}\n"); + let r = read(Format::Qir, module.as_bytes()).unwrap_or_else(|d| panic!("{d}\n{module}")); + assert!(r.qasm.contains("ry(1.5) q[0];"), "{}", r.qasm); + // c[2] is 1 exactly when c[0] equals c[1]. + let c = counts(&r.qasm, 400); + for k in c.keys() { + let b: Vec = k.chars().collect(); // c[2] c[1] c[0] + assert_eq!(b[0] == '1', b[1] == b[2], "{k} in {c:?}\n{}", r.qasm); + } + // Without the flag, the same computation is what the profile forbids, on its line. + let bare = adaptive(3, 3, &blocks, &[]); + let d = read(Format::Qir, bare.as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::PROFILE_FORBIDS, Some(4)), "{d}"); +} + +#[test] +fn what_each_profile_forbids_is_refused_with_its_code_and_line() { + // Base: a branch on a result. + let m = base(1, 1, &format!(" call void @__quantum__qis__h__body({})", q(0)), " call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)"); + let d = read(Format::Qir, m.as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::PROFILE_FORBIDS, Some(10)), "{d}"); + assert!(d.message.contains("base profile"), "{d}"); + // Base: a gate after a measurement. + let m = base(1, 1, " call void @__quantum__qis__mz__body(ptr null, ptr writeonly null)", &format!(" call void @__quantum__qis__x__body({})", q(0))); + let d = read(Format::Qir, m.as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::PROFILE_FORBIDS, Some(9)), "{d}"); + // Adaptive: a loop with no flag forbids it; with the flag, the reader does not take it. + let looped = format!(" br label %l\nl:\n call void @__quantum__qis__mz__body({}, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n br i1 %r, label %l, label %done\ndone:\n ret i64 0", q(0)); + let d = read(Format::Qir, adaptive(1, 1, &looped, &[]).as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::PROFILE_FORBIDS, Some(8)), "{d}"); + let d = read(Format::Qir, adaptive(1, 1, &looped, &["!{i32 1, !\"backwards_branching\", i2 2}"]).as_bytes()).unwrap_err(); + assert_eq!(code(&d), diag::CAPABILITY_NOT_TAKEN, "{d}"); + // Adaptive: a result read, measured again, then branched on. + let stale = format!(" call void @__quantum__qis__mz__body({}, ptr writeonly null)\n %r = call i1 @__quantum__rt__read_result(ptr readonly null)\n call void @__quantum__qis__mz__body({}, ptr writeonly null)\n br i1 %r, label %a, label %b\na:\n br label %b\nb:\n ret i64 0", q(0), q(1)); + let d = read(Format::Qir, adaptive(2, 1, &stale, &[]).as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::STALE_READ, Some(7)), "{d}"); + // An intrinsic the reader does not know; an index past the declared count. + let d = read(Format::Qir, base(1, 0, &format!(" call void @__quantum__qis__foo__body({})", q(0)), "").as_bytes()).unwrap_err(); + assert_eq!((code(&d), d.line()), (diag::INSTRUCTION_UNKNOWN, Some(6)), "{d}"); + let d = read(Format::Qir, base(1, 0, &format!(" call void @__quantum__qis__h__body({})", q(4)), "").as_bytes()).unwrap_err(); + assert_eq!(code(&d), diag::OPERAND, "{d}"); + // A profile the reader does not take; a major version it does not take. + let d = read(Format::Qir, base(1, 0, "", "").replace("base_profile", "custom").as_bytes()).unwrap_err(); + assert_eq!(code(&d), diag::PROFILE_UNKNOWN, "{d}"); + let d = read(Format::Qir, base(1, 0, "", "").replace("i32 2}", "i32 3}").as_bytes()).unwrap_err(); + assert_eq!(code(&d), diag::FORMAT_VERSION, "{d}"); + // A module that names no profile reads under the one its instructions need, and says so. + let r = read(Format::Qir, base(1, 0, &format!(" call void @__quantum__qis__h__body({})", q(0)), "").replace(" \"qir_profiles\"=\"base_profile\"", "").as_bytes()).unwrap(); + assert!(r.notes.iter().any(|n| n.code == diag::FORMAT_INCOMPLETE), "{:?}", r.notes); + // Not QIR at all. + assert_eq!(code(&read(Format::Qir, b"this is not llvm").unwrap_err()), diag::FORMAT_UNREAD); +} + +// --------------------------------------------------------------------------- +// QIR, written +// --------------------------------------------------------------------------- + +/// Every gate of the language, alone on qubits in a superposition, written +/// as QIR and read back: the same unitary (decided exactly, on every basis +/// state). +#[test] +fn every_gate_is_written_exactly() { + let gates = [ + "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.3) q[0];", "ry(-0.7) q[1];", "rz(1.9) q[2];", "p(0.45) q[0];", "p(pi/4) q[1];", "p(-pi/2) q[2];", "U(0.2, 0.9, -1.4) q[1];", + "cx q[0], q[1];", "cy q[1], q[2];", "cz q[2], q[0];", "ch q[0], q[2];", "cp(0.8) q[1], q[0];", "crx(0.6) q[0], q[1];", "cry(-1.2) q[2], q[1];", + "crz(2.2) q[1], q[2];", "cu(0.3, 0.4, 0.5, 0.6) q[0], q[2];", "swap q[0], q[2];", "iswap q[1], q[0];", "rxx(0.7) q[0], q[1];", "ryy(1.1) q[1], q[2];", + "rzz(-0.4) q[2], q[0];", "ccx q[0], q[1], q[2];", "ccz q[2], q[0], q[1];", "cswap q[1], q[0], q[2];", + ]; + for g in gates { + let src = format!("OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nry(0.37) q[1];\nrx(1.1) q[2];\n{g}\nc = measure q;\n"); + let p = qasm::read(&src).unwrap().program; + let e = write(Format::Qir, &p, None).unwrap(); + let back = qasm_of(Format::Qir, &e.bytes); + let (v, how) = verdict(&back, &src); + assert_eq!((v.as_str(), how.as_str()), ("equivalent", "exact"), "{g} written as\n{}\nread back as\n{back}", String::from_utf8_lossy(&e.bytes)); + // And through jeff. + let j = write(Format::Jeff, &p, None).unwrap(); + let back = qasm_of(Format::Jeff, &j.bytes); + let (v, how) = verdict(&back, &src); + assert_eq!((v.as_str(), how.as_str()), ("equivalent", "exact"), "{g} through jeff reads back as\n{back}"); + } +} + +/// A Clifford+T program is written in Clifford+T intrinsics, with no +/// rotation, and reads back to the bit-exact method; a module written twice +/// is the same bytes. +#[test] +fn clifford_t_stays_clifford_t_and_writing_is_deterministic() { + let src = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\np(pi/4) q[1];\np(3*pi/4) q[2];\ncx q[0], q[1];\nccx q[0], q[1], q[2];\ns q[2];\nc = measure q;\n"; + let p = qasm::read(src).unwrap().program; + let e = write(Format::Qir, &p, None).unwrap(); + let text = String::from_utf8(e.bytes.clone()).unwrap(); + assert!(!text.contains("__rz__") && !text.contains("__rx__") && !text.contains("__ry__"), "{text}"); + assert!(text.contains("\"qir_profiles\"=\"base_profile\"") && text.contains("!{i32 1, !\"qir_major_version\", i32 2}"), "{text}"); + let back = qasm_of(Format::Qir, &e.bytes); + qasm::exact_circuit(&back).unwrap_or_else(|d| panic!("{d}\n{back}")); + assert_eq!(write(Format::Qir, &p, None).unwrap().bytes, e.bytes); + assert_eq!(write(Format::Jeff, &p, None).unwrap().bytes, write(Format::Jeff, &p, None).unwrap().bytes); +} + +/// A program that measures in the middle and branches is written under the +/// adaptive profile and reads back step for step; the base profile refuses it. +#[test] +fn a_program_that_branches_is_written_under_the_adaptive_profile() { + let src = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nx q[0];\nh q[1];\ncx q[1], q[2];\ncx q[0], q[1];\nh q[0];\nc[0] = measure q[0];\nc[1] = measure q[1];\nif (c[1]) {\n x q[2];\n}\nif (c[0] == 1) {\n z q[2];\n} else {\n reset q[0];\n}\nif (c == 2) {\n y q[0];\n}\nc[2] = measure q[2];\n"; + let p = qasm::read(src).unwrap().program; + assert!(p.dynamic.is_some()); + let d = write(Format::Qir, &p, Some(Profile::Base)).unwrap_err(); + assert_eq!(code(&d), diag::WRITE_CANNOT); + let e = write(Format::Qir, &p, None).unwrap(); + assert_eq!(e.summary["profile"], json!("adaptive")); + let text = String::from_utf8(e.bytes.clone()).unwrap(); + assert!(text.contains("@__quantum__rt__read_result") && text.contains("\"qir_profiles\"=\"adaptive_profile\""), "{text}"); + let back = qasm_of(Format::Qir, &e.bytes); + let c = convert(Format::Qasm, Format::Qir, src.as_bytes(), None, true).unwrap(); + assert_eq!(c.check["verdict"]["verdict"], json!("equivalent"), "{}\n{back}", c.check); + // The teleported qubit reads 1 on every shot, as the source does. + for k in counts(&back, 300).keys() { + assert!(k.starts_with('1'), "{k}\n{back}"); + } + // jeff writes no branches. + assert_eq!(code(&write(Format::Jeff, &p, None).unwrap_err()), diag::WRITE_CANNOT); +} + +// --------------------------------------------------------------------------- +// jeff, built by hand +// --------------------------------------------------------------------------- + +/// A jeff module built field by field from the schema, independent of the +/// writer: one function, its values' types and its operations. +struct Hand { + values: Vec, + ops: Vec, + strings: Vec, + sources: Vec, + targets: Vec, + version: (u32, u32), +} + +const QUBIT: u16 = 0; +const INT: u16 = 2; +const FLOAT: u16 = 4; + +impl Hand { + fn new() -> Hand { + Hand { values: vec![], ops: vec![], strings: vec!["main".into()], sources: vec![], targets: vec![], version: (0, 3) } + } + fn value(&mut self, kind: u16, width: u8) -> u32 { + let mut t = Out::new(2, 0); + t.set_u16(0, kind); + match kind { + INT => t.set_u8(2, width), + FLOAT => t.set_u16(1, 1), + _ => {} + } + let mut v = Out::new(0, 2); + v.ptrs[0] = Ptr::Struct(Box::new(t)); + self.values.push(v); + self.values.len() as u32 - 1 + } + fn op(&mut self, inputs: Vec, outputs: Vec, kind: u16, body: Out) { + let mut o = Out::new(1, 4); + o.set_u16(0, kind); + o.ptrs[0] = Ptr::U32s(inputs); + o.ptrs[1] = Ptr::U32s(outputs); + o.ptrs[3] = Ptr::Struct(Box::new(body)); + self.ops.push(o); + } + fn alloc(&mut self) -> u32 { + let v = self.value(QUBIT, 0); + let mut b = Out::new(1, 1); + b.set_u16(0, 0); + self.op(vec![], vec![v], 0, b); + v + } + fn float(&mut self, x: f64) -> u32 { + let v = self.value(FLOAT, 0); + let mut b = Out::new(2, 0); + b.set_u16(2, 1); + b.set_f64(1, x); + self.op(vec![], vec![v], 4, b); + v + } + /// A gate: `which` 0 well-known (`code` its enum value), 1 custom (`code` + /// its name's string), 2 a Pauli-product rotation (`paulis`). + #[allow(clippy::too_many_arguments)] + fn gate(&mut self, which: u16, code: u16, paulis: Vec, qubits: &[u32], controls: &[u32], params: &[u32], adjoint: bool, power: u8, custom: (u8, u8)) -> Vec { + let mut g = Out::new(2, 1); + g.set_u16(1, which); + g.set_u16(0, code); + g.set_u8(4, custom.0); + g.set_u8(5, custom.1); + g.set_u8(6, controls.len() as u8); + g.set_bool(56, adjoint); + g.set_u8(8, power); + if which == 2 { + g.ptrs[0] = Ptr::U16s(paulis); + } + let mut b = Out::new(1, 1); + b.set_u16(0, 6); + b.ptrs[0] = Ptr::Struct(Box::new(g)); + let inputs: Vec = qubits.iter().chain(controls).chain(params).copied().collect(); + let outputs: Vec = qubits.iter().chain(controls).map(|_| self.value(QUBIT, 0)).collect(); + self.op(inputs, outputs.clone(), 0, b); + outputs + } + fn measure(&mut self, q: u32) -> u32 { + let r = self.value(INT, 1); + let mut b = Out::new(1, 1); + b.set_u16(0, 3); + self.op(vec![q], vec![r], 0, b); + r + } + /// A 32-bit integer constant. + fn int32(&mut self, v: u32) -> u32 { + let x = self.value(INT, 32); + let mut b = Out::new(2, 0); + b.set_u16(1, 3); + b.set_u32(1, v); + self.op(vec![], vec![x], 2, b); + x + } + /// A register of `size` qubits (`size` a value), allocated. + fn qureg_alloc(&mut self, size: u32) -> u32 { + let mut t = Out::new(2, 0); + t.set_u16(0, 1); + let mut v = Out::new(0, 2); + v.ptrs[0] = Ptr::Struct(Box::new(t)); + self.values.push(v); + let r = self.values.len() as u32 - 1; + let mut b = Out::new(1, 0); + b.set_u16(0, 0); + self.op(vec![size], vec![r], 1, b); + r + } + fn bytes(&self) -> Vec { + let mut region = Out::new(0, 4); + region.ptrs[0] = Ptr::U32s(self.sources.clone()); + region.ptrs[1] = Ptr::U32s(self.targets.clone()); + region.ptrs[2] = Ptr::Structs { data_words: 1, ptr_count: 4, items: self.ops.clone() }; + let mut f = Out::new(1, 3); + f.ptrs[0] = Ptr::Struct(Box::new(region)); + f.ptrs[1] = Ptr::Structs { data_words: 0, ptr_count: 2, items: self.values.clone() }; + let mut m = Out::new(2, 5); + m.set_u32(0, self.version.0); + m.set_u32(2, self.version.1); + m.ptrs[0] = Ptr::Structs { data_words: 1, ptr_count: 3, items: vec![f] }; + m.ptrs[1] = Ptr::Texts(self.strings.clone()); + super::capnp::write(&m) + } +} + +/// Well-known gates with their controls after their targets, as the schema +/// says: `x` with target q0 and control q1 is `cx q[1], q[0]`; `adjoint` is +/// the inverse and `power` repeats it; `r1` is the phase gate and `u` is +/// OpenQASM's `U`. +const WELL_KNOWN_REF: &str = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nh q[0];\ncx q[0], q[1];\nrx(-0.9) q[2];\nsdg q[0];\nU(0.2, 0.4, -0.6) q[1];\nctrl(2) @ p(0.9) q[0], q[1], q[2];\np(0.25) q[0];\n"; + +/// The module of `well_known_gates_read_with_the_schemas_operand_order`. +fn well_known_module() -> Vec { + let mut h = Hand::new(); + let (a, b, c) = (h.alloc(), h.alloc(), h.alloc()); + let a = h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0))[0]; // h q0 + let o = h.gate(0, 0, vec![], &[b], &[a], &[], false, 0, (0, 0)); // x on q1, control q0 + let (b, a) = (o[0], o[1]); + let t = h.float(0.3); + let c = h.gate(0, 6, vec![], &[c], &[], &[t], true, 3, (0, 0))[0]; // (rx(0.3)†)³ on q2 + let s = h.gate(0, 3, vec![], &[a], &[], &[], true, 1, (0, 0))[0]; // s† on q0 + let (t1, t2, t3) = (h.float(0.2), h.float(0.4), h.float(-0.6)); + let u = h.gate(0, 10, vec![], &[b], &[], &[t1, t2, t3], false, 0, (0, 0))[0]; // U on q1 + let ph = h.float(0.9); + let o = h.gate(0, 5, vec![], &[c], &[s, u], &[ph], false, 0, (0, 0)); // p(0.9) on q2 under q0, q1 + let g = h.float(0.25); + h.gate(0, 13, vec![], &[], &[o[1]], &[g], false, 0, (0, 0)); // gphase under q0: a phase on q0 + h.bytes() +} + +#[test] +fn well_known_gates_read_with_the_schemas_operand_order() { + let r = read(Format::Jeff, &well_known_module()).unwrap(); + let want = WELL_KNOWN_REF; + assert_eq!(verdict(&r.qasm, want), ("equivalent".into(), "exact".into()), "{}", r.qasm); + assert_eq!(r.summary["qubits"], json!(3)); +} + +/// A Pauli-product rotation is `exp(iθP)`, as the schema defines it: its +/// unitary, on every basis state, is `cos θ I + i sin θ P`. +#[test] +fn a_pauli_product_rotation_is_exp_i_theta_p() { + use crate::gates::complex; + let theta = 0.37; + for paulis in [vec![3u16], vec![1], vec![2], vec![1, 2], vec![3, 0, 1], vec![2, 2, 3], vec![0, 0]] { + let n = paulis.len() as u32; + let mut h = Hand::new(); + let qs: Vec = (0..n).map(|_| h.alloc()).collect(); + let t = h.float(theta); + h.gate(2, 0, paulis.clone(), &qs, &[], &[t], false, 0, (0, 0)); + let r = read(Format::Jeff, &h.bytes()).unwrap(); + let p = qasm::read(&r.qasm).unwrap().program; + // The reference, entry by entry: (cos θ I + i sin θ P)|k⟩. + let one = |p: u16, bit: usize| -> (usize, (f64, f64)) { + match p { + 0 => (bit, (1.0, 0.0)), + 1 => (bit ^ 1, (1.0, 0.0)), + 2 => (bit ^ 1, if bit == 0 { (0.0, 1.0) } else { (0.0, -1.0) }), + _ => (bit, if bit == 0 { (1.0, 0.0) } else { (-1.0, 0.0) }), + } + }; + let gphase_only = paulis.iter().all(|&x| x == 0); + for k in 0..1usize << n { + let mut sv = crate::quantum_sv::StateVector::zero(n).unwrap(); + sv.re[0] = 0.0; + sv.re[k] = 1.0; + for g in p.matrices() { + sv.apply(&g).unwrap(); + } + // P|k⟩: qubit 0 is the most significant bit. + let (mut idx, mut amp) = (0usize, (1.0f64, 0.0f64)); + for (q, &pq) in paulis.iter().enumerate() { + let bit = k >> (n as usize - 1 - q) & 1; + let (b, a) = one(pq, bit); + idx |= b << (n as usize - 1 - q); + amp = (amp.0 * a.0 - amp.1 * a.1, amp.0 * a.1 + amp.1 * a.0); + } + let (c, s) = (theta.cos(), theta.sin()); + let mut want = vec![complex(0.0, 0.0); 1 << n]; + want[k].re += c; + // i sin θ · amp + want[idx].re += -s * amp.1; + want[idx].im += s * amp.0; + if gphase_only { + // A global phase is dropped by the reader: compare up to it. + continue; + } + for (i, w) in want.iter().enumerate() { + assert!((sv.re[i] - w.re).abs() < 1e-12 && (sv.im[i] - w.im).abs() < 1e-12, "{paulis:?} |{k}⟩ entry {i}: ({}, {}) against ({}, {})\n{}", sv.re[i], sv.im[i], w.re, w.im, r.qasm); + } + } + assert!(r.notes.iter().any(|d| d.code == diag::JEFF_PPR), "{:?}", r.notes); + } +} + +/// Custom gates read only when they name a gate of the standard library with +/// its qubits and parameters, and say so; the rest are refused. +fn custom_module() -> Vec { + let mut h = Hand::new(); + h.strings.extend(["H".to_string(), "CX".into(), "Rx".into(), "Hadamard".into()]); + let (a, b) = (h.alloc(), h.alloc()); + let a = h.gate(1, 1, vec![], &[a], &[], &[], false, 0, (1, 0))[0]; + let o = h.gate(1, 2, vec![], &[a, b], &[], &[], false, 0, (2, 0)); + let t = h.float(0.5); + h.gate(1, 3, vec![], &[o[1]], &[], &[t], false, 0, (1, 1)); + h.bytes() +} + +/// A custom gate the library does not have (`name` 4, `Hadamard`), or one of +/// its names with the wrong qubits (`CX` on one). +fn custom_refused_module(name: u16, qubits: u8) -> Vec { + let mut h2 = Hand::new(); + h2.strings.extend(["H".to_string(), "CX".into(), "Rx".into(), "Hadamard".into()]); + let x = h2.alloc(); + h2.gate(1, name, vec![], &[x], &[], &[], false, 0, (qubits, 0)); + h2.bytes() +} + +fn qubit_twice_module() -> Vec { + let mut h = Hand::new(); + let a = h.alloc(); + h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0)); + h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0)); + h.bytes() +} + +/// A register whose size is the largest 32-bit integer. +fn huge_register_module() -> Vec { + let mut h = Hand::new(); + let n = h.int32(u32::MAX); + h.qureg_alloc(n); + h.bytes() +} + +/// A register's size reaches no allocation before the qubit cap: one of +/// 2^32 − 1 qubits is refused as more qubits than the reader takes, not +/// allocated (34 GB, which aborts a process and panics in a page). +#[test] +fn a_register_too_large_is_refused_before_it_is_allocated() { + let d = read(Format::Jeff, &huge_register_module()).unwrap_err(); + assert_eq!(code(&d), diag::TOO_MANY_QUBITS, "{d}"); + assert!(d.message.contains("operation 1"), "{d}"); +} + +fn old_version_module() -> Vec { + let mut h = Hand::new(); + h.alloc(); + h.version = (0, 1); + h.bytes() +} + +/// An angle computed from a measured bit: not known on reading. +fn angle_from_measurement_module() -> Vec { + let mut h = Hand::new(); + let (a, b) = (h.alloc(), h.alloc()); + let a = h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0))[0]; + let r = h.measure(a); + let f = h.value(FLOAT, 0); + let mut conv = Out::new(2, 0); + conv.set_u16(1, 34); + h.op(vec![r], vec![f], 2, conv); + h.gate(0, 6, vec![], &[b], &[], &[f], false, 0, (0, 0)); + h.bytes() +} + +/// A Pauli-product rotation on three qubits, `exp(0.37i X⊗Y⊗Z)`. +fn ppr_module() -> Vec { + let mut h = Hand::new(); + let qs: Vec = (0..3).map(|_| h.alloc()).collect(); + let t = h.float(0.37); + h.gate(2, 0, vec![1, 2, 3], &qs, &[], &[t], false, 0, (0, 0)); + h.bytes() +} + +#[test] +fn custom_gates_are_read_by_a_standard_name_or_refused() { + let r = read(Format::Jeff, &custom_module()).unwrap(); + assert_equivalent(&r.qasm, "OPENQASM 3.0; include \"stdgates.inc\"; qubit[2] q; h q[0]; cx q[0], q[1]; rx(0.5) q[1];"); + assert_eq!(r.notes.iter().filter(|d| d.code == diag::JEFF_CUSTOM_NAMED).count(), 3, "{:?}", r.notes); + // A name the library does not have, and a library name with the wrong qubits. + for (name, qubits) in [(4u16, 1u8), (2, 1)] { + let d = read(Format::Jeff, &custom_refused_module(name, qubits)).unwrap_err(); + assert_eq!(code(&d), diag::JEFF_CUSTOM, "{d}"); + assert!(d.message.contains("operation 1"), "where it stands: {d}"); + } +} + +/// The bits the entry point returns are the program's, in its order; a +/// switch on a measured bit is a condition. +#[test] +fn returned_bits_and_a_switch_on_a_measurement() { + let mut h = Hand::new(); + let (a, b) = (h.alloc(), h.alloc()); + let a = h.gate(0, 9, vec![], &[a], &[], &[], false, 0, (0, 0))[0]; + let b = h.gate(0, 0, vec![], &[b], &[], &[], false, 0, (0, 0))[0]; // x on q1: always 1 + let rb = h.measure(b); // measured first: bit 0 in measurement order + let ra = h.measure(a); + h.targets = vec![ra, rb]; // returned as (a, b): c[0] is a, c[1] is b + let r = read(Format::Jeff, &h.bytes()).unwrap(); + assert_eq!(r.summary["bits_returned"], json!(true)); + for k in counts(&r.qasm, 200).keys() { + assert!(k.starts_with('1'), "c[1] holds q1, always 1: {k}\n{}", r.qasm); + } +} + +#[test] +fn jeff_refusals_name_their_code_and_place() { + // A qubit used twice. + let d = read(Format::Jeff, &qubit_twice_module()).unwrap_err(); + assert_eq!(code(&d), diag::JEFF_LINEAR, "{d}"); + assert!(d.message.contains("operation 2") && d.message.contains("`main`"), "{d}"); + // A version the reader does not take. + assert_eq!(code(&read(Format::Jeff, &old_version_module()).unwrap_err()), diag::FORMAT_VERSION); + // An angle from a measurement. + assert_eq!(code(&read(Format::Jeff, &angle_from_measurement_module()).unwrap_err()), diag::JEFF_NOT_KNOWN); + // Not a message; every prefix of a module refused, never read past its end. + assert_eq!(code(&read(Format::Jeff, b"\x00\x00").unwrap_err()), diag::FORMAT_UNREAD); + let p = qasm::read("OPENQASM 3.0; include \"stdgates.inc\"; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; rz(0.2) q[1]; c = measure q;").unwrap().program; + let bytes = write(Format::Jeff, &p, None).unwrap().bytes; + for n in 0..bytes.len() { + assert!(read(Format::Jeff, &bytes[..n]).is_err(), "a prefix of {n} bytes read"); + } + // Any byte changed: refused or read, never a panic. + for i in 0..bytes.len() { + for x in [0x01u8, 0x80, 0xff] { + let mut b = bytes.clone(); + b[i] ^= x; + let _ = read(Format::Jeff, &b); + } + } +} + +// --------------------------------------------------------------------------- +// Conversion, as every surface asks for it +// --------------------------------------------------------------------------- + +#[test] +fn convert_json_carries_a_program_between_every_pair_and_checks_it() { + let src = "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\ncx q[0], q[1];\nrz(0.123) q[1];\nccx q[0], q[1], q[2];\nc = measure q;\n"; + let to_qir = convert_json(&json!({ "program": src, "to": "qir" })).unwrap(); + assert_eq!((to_qir["format"].as_str(), to_qir["from"].as_str(), to_qir["check"]["verdict"]["verdict"].as_str()), (Some(FORMAT), Some("qasm"), Some("equivalent")), "{to_qir}"); + let to_jeff = convert_json(&json!({ "program": to_qir["program"], "to": "jeff" })).unwrap(); + assert_eq!(to_jeff["from"], json!("qir"), "found from the bytes"); + assert_eq!(to_jeff["check"]["verdict"]["verdict"], json!("equivalent"), "{to_jeff}"); + let back = convert_json(&json!({ "program_base64": to_jeff["program_base64"], "to": "qasm" })).unwrap(); + assert_eq!(back["from"], json!("jeff")); + assert_eq!(back["check"]["verdict"]["verdict"], json!("equivalent"), "{back}"); + assert_equivalent(back["program"].as_str().unwrap(), src); + // Requests that do not read are the job layer's refusals. + for bad in [json!({ "program": src }), json!({ "program": src, "to": "pdf" }), json!({ "to": "qir" }), json!({ "program": src, "to": "jeff", "profile": "base" })] { + let e = convert_json(&bad).unwrap_err(); + assert!(e.ends_with("]"), "{e}"); + } + // A job may carry its program in another format. + let job = expand(&json!({ "source": { "format": "qir", "program": to_qir["program"] }, "shots": 10 })).unwrap().unwrap(); + assert_equivalent(job["qasm"].as_str().unwrap(), src); + assert!(expand(&json!({ "qasm": src })).unwrap().is_none()); + assert!(expand(&json!({ "qasm": src, "source": { "program": "x" } })).is_err()); + // A job read already reads as it is; one whose qasm is another program is refused. + assert!(expand(&job).unwrap().is_none()); + let mut other = job.clone(); + other["qasm"] = json!(src); + assert!(expand(&other).unwrap_err().ends_with("[J003]")); +} + +#[test] +fn base64_round_trips() { + for n in 0..40usize { + let b: Vec = (0..n).map(|i| (i * 37 + 11) as u8).collect(); + let e = encode_base64(&b); + assert_eq!(e.len(), n.div_ceil(3) * 4); + assert_eq!(decode_base64(&e).unwrap(), b); + } + assert_eq!(encode_base64(b"jeff"), "amVmZg=="); + assert!(decode_base64("am*f").is_err()); +} + +/// The sniffer and the names agree with the formats. +#[test] +fn formats_are_named_and_found() { + for &f in Format::all() { + assert_eq!(Format::parse(f.name()), Some(f)); + assert_eq!(Format::from_extension(&format!("x.{}", f.extension())), Some(f)); + } + assert_eq!(Format::sniff(BELL_BASE.as_bytes()), Format::Qir); + assert_eq!(Format::sniff(b"OPENQASM 3.0; qubit q;"), Format::Qasm); + let p = qasm::read("OPENQASM 3.0; qubit[1] q; h q[0];").unwrap().program; + assert_eq!(Format::sniff(&write(Format::Jeff, &p, None).unwrap().bytes), Format::Jeff); +} + +/// The conformance set's modules made here (`wai/quantum-conformance/formats/`): +/// the hand-built jeff modules, and a QIR 1 module with typed pointers from +/// the crate's earlier writer, with its reference from the toolchain's own +/// OpenQASM export. With `WAI_FORMATS_VECTORS_WRITE=1` this writes them; +/// otherwise it holds the files to them, so neither can drift. +#[cfg(not(target_os = "wasi"))] +// It writes QIR 1 with the dyadic writer, which `quantum_qir` holds. +#[cfg(feature = "quantum_qir")] +#[test] +fn the_conformance_set_holds_these_modules() { + use crate::quantum_toolchain as qt; + let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/formats"); + let qft = qt::build_algorithm("qft", 3, 0).unwrap(); + let files: Vec<(&str, Vec)> = vec![ + ("jeff/controls_after_targets.jeff", well_known_module()), + ("jeff/controls_after_targets.ref.qasm", WELL_KNOWN_REF.as_bytes().to_vec()), + ("jeff/custom_named.jeff", custom_module()), + ("jeff/pauli_product.jeff", ppr_module()), + ("jeff/refuse/custom_unknown.jeff", custom_refused_module(4, 1)), + ("jeff/refuse/custom_wrong_qubits.jeff", custom_refused_module(2, 1)), + ("jeff/refuse/qubit_twice.jeff", qubit_twice_module()), + ("jeff/refuse/old_version.jeff", old_version_module()), + ("jeff/refuse/angle_from_measurement.jeff", angle_from_measurement_module()), + ("jeff/refuse/truncated.jeff", custom_module()[..200].to_vec()), + ("jeff/refuse/register_too_large.jeff", huge_register_module()), + ("qir/qft3_typed.ll", crate::quantum_qir::to_qir(&qft, true).into_bytes()), + ("qir/qft3_typed.ref.qasm", qt::to_qasm(&qft).into_bytes()), + ]; + let write = std::env::var_os("WAI_FORMATS_VECTORS_WRITE").is_some(); + for (f, bytes) in files { + let p = dir.join(f); + if write { + std::fs::create_dir_all(p.parent().unwrap()).unwrap(); + std::fs::write(&p, &bytes).unwrap(); + } else { + assert_eq!(std::fs::read(&p).unwrap_or_default(), bytes, "{f} differs from what this test makes; write it with WAI_FORMATS_VECTORS_WRITE=1"); + } + } +} diff --git a/wai/wai-quantum/src/engine/mod.rs b/wai/wai-quantum/src/engine/mod.rs index 1a71286a5..8d710d86b 100644 --- a/wai/wai-quantum/src/engine/mod.rs +++ b/wai/wai-quantum/src/engine/mod.rs @@ -75,6 +75,10 @@ //! frontier between qubits and time; every estimate sealed in the //! resource-estimate receipt family, with the program's digest and its ε //! budget. The logical-counts and routine documents go in and out. +//! - [`formats`]: programs in other formats, QIR (under its base and +//! adaptive profiles) and jeff, read to the one program and written from +//! it, every construct a reader cannot hold refused with its code and its +//! place, and each conversion checked by the equivalence ladder. //! - [`rewrite`]: rewrites that keep a circuit's meaning, each checked on //! the qubits it touches: a gate and its inverse cancel, rotations merge, //! Hadamards conjugate, a gate is carried past what it commutes with. A @@ -196,6 +200,7 @@ pub mod discover; pub mod energy; pub mod equiv; pub mod estimate; +pub mod formats; pub mod guide; pub mod latex; #[cfg(feature = "quantum_noise")] diff --git a/wai/wai-quantum/src/engine/pulse/tests.rs b/wai/wai-quantum/src/engine/pulse/tests.rs index 4c581eba9..5e5ac1d38 100644 --- a/wai/wai-quantum/src/engine/pulse/tests.rs +++ b/wai/wai-quantum/src/engine/pulse/tests.rs @@ -122,6 +122,8 @@ fn what_a_pulse_job_cannot_be_is_refused_with_its_code() { } } +// It seals its answers, so it needs the receipts. +#[cfg(feature = "quantum_receipt")] #[test] fn a_pulse_job_is_checked_by_running_it_again() { let req = job_of(json!({})); @@ -149,6 +151,8 @@ fn a_pulse_job_is_checked_by_running_it_again() { ok(Registry::engine().check("pulse.v1.json", &seal(&body, call("check", &body).unwrap(), &key())), "the check"); } +// It seals its answers, so it needs the receipts. +#[cfg(feature = "quantum_receipt")] #[test] fn a_schedule_is_drawn_with_its_digests() { let req = json!({ "qasm": XM, "pulse": { "dt_ns": 0.5 } }); diff --git a/wai/wai-quantum/src/qasm.rs b/wai/wai-quantum/src/qasm.rs index deb47dc3a..2c56d3846 100644 --- a/wai/wai-quantum/src/qasm.rs +++ b/wai/wai-quantum/src/qasm.rs @@ -716,7 +716,7 @@ const DYADIC_TOLERANCE: f64 = 1e-14; /// `2πm/2^k` for the smallest `k ≤ 32` within [`DYADIC_TOLERANCE`] turns: /// `m mod 2^k` scaled to denominator `2^32`. -fn dyadic(angle: f64) -> Option { +pub(crate) fn dyadic(angle: f64) -> Option { let t = (angle / std::f64::consts::TAU).rem_euclid(1.0); for k in 0..=32u32 { let scale = (1u64 << k) as f64; diff --git a/wai/wai-quantum/src/qasm/diag.rs b/wai/wai-quantum/src/qasm/diag.rs index ebf0b9a77..7cc9f3b0c 100644 --- a/wai/wai-quantum/src/qasm/diag.rs +++ b/wai/wai-quantum/src/qasm/diag.rs @@ -274,6 +274,27 @@ table! { // Outputs. OUTPUT = "Q600", Carried, "An `output` declaration, carried into the result: its bits (their places in each outcome) or its value."; OUTPUT_UNKNOWN = "Q601", Refused, "An `output` that is not a bit and whose value depends on a measurement at the end."; + + // Other formats: QIR and jeff, read to a program and written from one (engine::formats). + FORMAT_UNREAD = "Q700", Refused, "A file that does not read in the format named: QIR that is not LLVM text with one entry point, or jeff that is not a Cap'n Proto message of the jeff schema (a pointer outside the message, a struct or list of the wrong shape, more than the reader takes); the message says what and where."; + FORMAT_VERSION = "Q701", Refused, "A format version the reader does not take: QIR whose `qir_major_version` is not 1 or 2, or jeff other than 0.2 and 0.3."; + FORMAT_INCOMPLETE = "Q702", Warned, "A QIR module that leaves out what its profile requires (the `qir_profiles` attribute, the version flags, `required_num_qubits`, `required_num_results`, the `entry_point` attribute of its only function): read from what it holds, and said so."; + PROFILE_UNKNOWN = "Q703", Refused, "A QIR profile the reader does not take: `qir_profiles` names neither `base_profile` nor `adaptive_profile`."; + PROFILE_FORBIDS = "Q704", Refused, "What the module's own QIR profile forbids: under the base profile a branch, a classical instruction, reading a result, or a gate or reset on a qubit after its measurement; under the adaptive profile an optional capability its module flags do not declare (computation on integers or floats of a type, IR-defined functions, loops, `switch`, several returns, dynamic qubits or results, arrays)."; + CAPABILITY_NOT_TAKEN = "Q705", Refused, "A QIR capability the module may use that this reader does not take: loops, `switch`, `phi`, IR-defined functions, dynamic allocation of qubits or results, arrays, controlled forms that take their controls in an array, or a classical value computed from a measurement anywhere but a branch's condition."; + INSTRUCTION_UNKNOWN = "Q706", Refused, "An instruction or function the reader does not know: a quantum intrinsic outside the gates it reads (`docs/formats.md` lists them), a runtime function it does not take, an LLVM instruction no profile lists, or a function a module declares and does not define."; + OPERAND = "Q707", Refused, "A QIR operand that does not read: a qubit or result that is not a constant index, an index past `required_num_qubits` or `required_num_results`, or an angle that is not a finite number known on reading."; + STALE_READ = "Q708", Refused, "A value whose meaning the program's text cannot keep: in QIR a result read, measured again and only then branched on; in jeff a qubit the two sides of a branch on a measurement leave in different places."; + ARBITRARY_ANGLE = "Q709", Warned, "Rotations at angles that are not dyadic multiples of 2π, read at their exact doubles: the double-precision methods take them; the bit-exact method refuses them (Q500)."; + JEFF_CUSTOM = "Q710", Refused, "A jeff custom gate the reader does not know: its name is not a gate of the standard library (in any case), or its qubits or parameters are not that gate's."; + JEFF_CUSTOM_NAMED = "Q711", Warned, "A jeff custom gate named as a gate of the standard library (`H`, `CX`, `Rx`, in any case) with that gate's qubits and parameters: read as that gate, by its name alone."; + JEFF_PPR = "Q712", Lowered, "A jeff Pauli-product rotation, `exp(iθP)` as its schema defines it, lowered exactly to basis changes, a parity ladder and one `rz`."; + JEFF_LINEAR = "Q713", Refused, "A jeff value used where it cannot be: a qubit or register used twice, a value used before it is defined or never defined, a cycle, or a value of the wrong type or count for its operation."; + JEFF_NOT_KNOWN = "Q714", Refused, "A jeff value the reader needs on reading and cannot know: a gate angle, a loop's bounds or a `while` condition from a measurement, a register's size or index, a `switch` on a value that is neither known nor a measured bit, or integer arithmetic on a measured bit beyond not, and, or, xor, eq and select."; + JEFF_STRUCTURED = "Q715", Lowered, "jeff's structured control flow: a `for` or `while` decided on reading unrolled, a `switch` on a known value decided, a `switch` on a measured bit written as `if … else`, and a call of a function the module defines inlined."; + JEFF_ENTRY_QUBITS = "Q716", Warned, "A jeff entry point that takes qubits: they are the program's first qubits, starting in |0⟩, and the qubits it returns are not measured."; + WRITE_CANNOT = "Q720", Refused, "A program the format written cannot hold as it is: under QIR's base profile a measurement in the middle, a reset after a gate or a condition (write the adaptive profile), a loop in QIR, or in jeff a branch on a measured bit."; + WRITE_DROPPED = "Q721", Warned, "What the format read or written has no place for, left out and said so: delays, boxes, barriers and gate durations, calibrations, `output` declarations, and classical values recorded as output."; } #[cfg(test)] diff --git a/wai/wai-quantum/tests/formats_conformance.rs b/wai/wai-quantum/tests/formats_conformance.rs new file mode 100644 index 000000000..eb9d7ec32 --- /dev/null +++ b/wai/wai-quantum/tests/formats_conformance.rs @@ -0,0 +1,211 @@ +//! The conformance set for other formats (`wai/quantum-conformance/formats/`): +//! every QIR and jeff module read, and every OpenQASM source written, held +//! to what its manifest says: the program it reads to (the same circuit by +//! the equivalence ladder), the outcomes it gives, the notes, a round trip +//! back to its own format, the formats it writes to (the bytes of QIR +//! written pinned by their BLAKE3), and each refusal with its code and its +//! place. +//! +//! The cases come from `bundle.json`, which holds each file inline (text, or +//! base64 for jeff), so the same test runs where no file can be read (under +//! wasm32-wasip2, by wasmtime); natively, a second test holds the bundle +//! equal to the files. Run with `WAI_FORMATS_BUNDLE_WRITE=1` to write the +//! bundle from the files. + +#![cfg(feature = "quantum_engine")] + +use serde_json::{Value, json}; +use std::collections::BTreeSet; +use wai_quantum::engine::formats::{self, Format, Profile}; +use wai_quantum::engine::{equiv, job}; +use wai_quantum::qasm; + +const BUNDLE: &str = include_str!("../../quantum-conformance/formats/bundle.json"); + +fn input(case: &Value) -> Result, String> { + match (case["text"].as_str(), case["base64"].as_str()) { + (Some(t), None) => Ok(t.as_bytes().to_vec()), + (None, Some(b)) => formats::decode_base64(b), + _ => Err("a case holds its file as text or base64".into()), + } +} + +fn codes(notes: &[wai_quantum::qasm::Diagnostic]) -> Vec { + let mut v: Vec = notes.iter().map(|d| d.code.to_string()).collect(); + v.sort(); + v.dedup(); + v +} + +fn strings(v: &Value) -> Vec { + v.as_array().map(|a| a.iter().filter_map(Value::as_str).map(String::from).collect()).unwrap_or_default() +} + +fn check(case: &Value) -> Result<(), String> { + let format = Format::parse(case["format"].as_str().ok_or("no format")?).ok_or("an unknown format")?; + let bytes = input(case)?; + let e = &case["expect"]; + let reading = formats::read(format, &bytes); + if let Some(code) = e["refused"].as_str() { + let d = match reading { + Err(d) => d, + Ok(_) => return Err(format!("read, where it must be refused with {code}")), + }; + if d.code != code { + return Err(format!("refused with {}, not {code}: {d}", d.code)); + } + if let Some(l) = e["line"].as_u64() + && d.line() != Some(l as usize) + { + return Err(format!("refused on line {:?}, not {l}: {d}", d.line())); + } + if let Some(at) = e["at"].as_str() + && !d.message.contains(at) + { + return Err(format!("the refusal does not say where ({at}): {d}")); + } + return Ok(()); + } + let r = reading.map_err(|d| format!("does not read: {d}"))?; + let (got, want) = (codes(&r.notes), strings(&e["notes"])); + if got != want { + return Err(format!("notes {got:?}, not {want:?}: {:?}", r.notes.iter().map(ToString::to_string).collect::>())); + } + if let Some(p) = e["profile"].as_str() + && r.summary["profile"] != json!(p) + { + return Err(format!("read under the {} profile, not {p}", r.summary["profile"])); + } + let p = qasm::read(&r.qasm).map_err(|d| format!("the program read does not read: {d}"))?.program; + if let Some(d) = e["dynamic"].as_bool() + && p.dynamic.is_some() != d + { + return Err(format!("dynamic is {}, not {d}", p.dynamic.is_some())); + } + if let Some(n) = e["qubits"].as_u64() + && u64::from(p.n_qubits) != n + { + return Err(format!("{} qubits, not {n}", p.n_qubits)); + } + if let Some(reference) = case["reference"].as_str() { + let v = equiv::equivalent(&r.qasm, reference); + if v["verdict"] != "equivalent" { + return Err(format!("not the reference: {v}\n{}", r.qasm)); + } + if let Some(how) = e["how"].as_str() + && v["how"] != json!(how) + { + return Err(format!("the reference decided by {}, not {how}", v["how"])); + } + } + if e["exact"] == json!(true) { + qasm::exact_circuit(&r.qasm).map_err(|d| format!("the bit-exact method does not take it: {d}"))?; + } + if e.get("outcomes").is_some() || e.get("equal_bits").is_some() { + let (_, res) = job::plan_and_run(&json!({ "qasm": r.qasm, "shots": 512, "seed": 7 }), 1 << 30)?; + let seen: BTreeSet = res["counts"].as_object().ok_or("no counts")?.keys().cloned().collect(); + if let Some(o) = e.get("outcomes") { + let want: BTreeSet = strings(o).into_iter().collect(); + if seen != want { + return Err(format!("outcomes {seen:?}, not {want:?}")); + } + } + for pair in e["equal_bits"].as_array().into_iter().flatten() { + let (a, b) = (pair[0].as_u64().ok_or("a bit")? as usize, pair[1].as_u64().ok_or("a bit")? as usize); + for k in &seen { + let bit = |i: usize| k.as_bytes()[k.len() - 1 - i]; + if bit(a) != bit(b) { + return Err(format!("bits {a} and {b} differ in {k}")); + } + } + } + } + if e["round_trip"] == json!(true) { + let c = formats::convert(format, Format::Qasm, &bytes, None, true).map_err(|d| d.to_string())?; + if c.check["verdict"]["verdict"] != "equivalent" { + return Err(format!("written back to {} and read again, not the same: {}", format.name(), c.check)); + } + } + for w in e["writes"].as_array().into_iter().flatten() { + let to = Format::parse(w["to"].as_str().ok_or("writes: to")?).ok_or("writes: an unknown format")?; + let profile = w["profile_asked"].as_str().map(|s| Profile::parse(s).ok_or("an unknown profile")).transpose()?; + let c = formats::convert(format, to, &bytes, profile, true); + if let Some(code) = w["refused"].as_str() { + match c { + Err(d) if d.code == code => continue, + Err(d) => return Err(format!("writing {}: refused with {}, not {code}: {d}", to.name(), d.code)), + Ok(_) => return Err(format!("writing {}: written, where it must be refused with {code}", to.name())), + } + } + let c = c.map_err(|d| format!("writing {}: {d}", to.name()))?; + if c.check["verdict"]["verdict"] != "equivalent" { + return Err(format!("written to {} and read back, not the same: {}", to.name(), c.check)); + } + if let Some(how) = w["how"].as_str() + && c.check["verdict"]["how"] != json!(how) + { + return Err(format!("written to {}: checked by {}, not {how}", to.name(), c.check["verdict"]["how"])); + } + if let Some(p) = w["profile"].as_str() + && c.written["profile"] != json!(p) + { + return Err(format!("written under the {} profile, not {p}", c.written["profile"])); + } + let got = codes(&c.notes); + let want = strings(&w["notes"]); + if got != want { + return Err(format!("writing {}: notes {got:?}, not {want:?}", to.name())); + } + if let Some(h) = w["blake3"].as_str() { + let got = blake3::hash(&c.output).to_hex().to_string(); + if got != h { + return Err(format!("written to {}: BLAKE3 {got}, not {h}", to.name())); + } + } + } + Ok(()) +} + +#[test] +fn every_module_reads_and_writes_as_its_manifest_says() { + let bundle: Value = serde_json::from_str(BUNDLE).expect("bundle.json reads"); + let cases = bundle["cases"].as_array().expect("cases"); + assert!(cases.len() >= 40, "{} cases", cases.len()); + let failures: Vec = cases.iter().filter_map(|c| check(c).err().map(|e| format!("{}: {e}", c["file"].as_str().unwrap_or("?")))).collect(); + assert!(failures.is_empty(), "{} of {} cases:\n{}", failures.len(), cases.len(), failures.join("\n")); +} + +/// The bundle is the files: each case's file (text, or base64 for jeff) and +/// reference as the manifest names them, and the manifest's expectations. +#[cfg(not(target_os = "wasi"))] +#[test] +fn the_bundle_is_the_corpus() { + let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/formats"); + let manifest: Value = serde_json::from_str(&std::fs::read_to_string(dir.join("manifest.json")).unwrap()).unwrap(); + let cases: Vec = manifest["cases"] + .as_array() + .unwrap() + .iter() + .map(|c| { + let f = c["file"].as_str().unwrap(); + let bytes = std::fs::read(dir.join(f)).unwrap_or_else(|e| panic!("{f}: {e}")); + let mut d = json!({ "file": f, "format": c["format"], "expect": c["expect"] }); + if c["format"] == "jeff" { + d["base64"] = json!(formats::encode_base64(&bytes)); + } else { + d["text"] = json!(String::from_utf8(bytes).unwrap()); + } + if let Some(r) = c["reference"].as_str() { + d["reference"] = json!(std::fs::read_to_string(dir.join(r)).unwrap()); + } + d + }) + .collect(); + if std::env::var_os("WAI_FORMATS_BUNDLE_WRITE").is_some() { + let bundle = json!({ "format": manifest["format"], "about": "The manifest's cases with each file inline (text, or base64 for jeff), for builds that read no files.", "cases": cases }); + std::fs::write(dir.join("bundle.json"), serde_json::to_string_pretty(&bundle).unwrap() + "\n").unwrap(); + return; + } + let bundle: Value = serde_json::from_str(BUNDLE).unwrap(); + assert_eq!(bundle["cases"], Value::Array(cases), "bundle.json differs from the files; write it with WAI_FORMATS_BUNDLE_WRITE=1"); +} diff --git a/wai/wai-quantum/tests/formats_mutation.rs b/wai/wai-quantum/tests/formats_mutation.rs new file mode 100644 index 000000000..b9c95b60e --- /dev/null +++ b/wai/wai-quantum/tests/formats_mutation.rs @@ -0,0 +1,388 @@ +//! Hostile inputs for the readers of other formats: every QIR and jeff file +//! of the conformance set (`wai/quantum-conformance/formats/`), mutated by a +//! seeded generator, converted to OpenQASM with its check. Every input must +//! convert or be refused with a code of the reader's table, never panic or +//! abort, finish within [`MAX_SECONDS`], and allocate at most [`MAX_PEAK`] +//! bytes beyond what the test held before it. +//! +//! The mutations: bits flipped; a file cut short; the start of one file +//! spliced to the end of another; and every length, count and offset the +//! readers parse set to its largest and to values just past their bounds: +//! in a jeff module the segment table, each pointer's offset, struct sizes +//! and list counts, and each 16-, 32- and 64-bit field (register sizes, +//! loop bounds, array lengths, indices, angles); in a QIR module each number +//! (indices, counts, versions, angles), lines repeated and branches turned +//! back on themselves. +//! +//! The same cases run natively and under wasm32-wasip2, where an allocation +//! larger than the address space panics rather than failing lazily: the +//! seed and the count are fixed, so both builds see the same inputs. Under +//! `WAI_FORMATS_MUTATION_REPORT=1` it prints the slowest inputs. + +#![cfg(feature = "quantum_engine")] + +use serde_json::Value; +use std::alloc::{GlobalAlloc, Layout, System}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use wai_quantum::engine::formats::{self, Format}; + +/// The allocator, counting what is live and the most it has been. +struct Counting; + +static LIVE: AtomicUsize = AtomicUsize::new(0); +static PEAK: AtomicUsize = AtomicUsize::new(0); + +unsafe impl GlobalAlloc for Counting { + unsafe fn alloc(&self, l: Layout) -> *mut u8 { + let p = unsafe { System.alloc(l) }; + if !p.is_null() { + let now = LIVE.fetch_add(l.size(), Ordering::Relaxed) + l.size(); + PEAK.fetch_max(now, Ordering::Relaxed); + } + p + } + unsafe fn dealloc(&self, p: *mut u8, l: Layout) { + unsafe { System.dealloc(p, l) }; + LIVE.fetch_sub(l.size(), Ordering::Relaxed); + } + unsafe fn alloc_zeroed(&self, l: Layout) -> *mut u8 { + let p = unsafe { System.alloc_zeroed(l) }; + if !p.is_null() { + let now = LIVE.fetch_add(l.size(), Ordering::Relaxed) + l.size(); + PEAK.fetch_max(now, Ordering::Relaxed); + } + p + } + unsafe fn realloc(&self, p: *mut u8, l: Layout, size: usize) -> *mut u8 { + let q = unsafe { System.realloc(p, l, size) }; + if !q.is_null() { + if size >= l.size() { + let now = LIVE.fetch_add(size - l.size(), Ordering::Relaxed) + (size - l.size()); + PEAK.fetch_max(now, Ordering::Relaxed); + } else { + LIVE.fetch_sub(l.size() - size, Ordering::Relaxed); + } + } + q + } +} + +#[global_allocator] +static ALLOCATOR: Counting = Counting; + +const BUNDLE: &str = include_str!("../../quantum-conformance/formats/bundle.json"); + +/// The generator's seed. +const SEED: u64 = 0x5eed_1e55_f0f0_0018; +/// Mutated inputs made from each file. +const PER_FILE: usize = 24; +/// The jeff modules whose every 32-bit field is inflated in turn: those of +/// at most this many bytes. +const SWEEP_BYTES: usize = 12_288; +/// The longest one input may take, in seconds (a release build; the wasm32 +/// leg of CI is a release build with overflow checks on). +const MAX_SECONDS: f64 = 20.0; +/// The most one input may allocate beyond what was live before it. +const MAX_PEAK: usize = 1 << 30; + +struct Rng(u64); + +impl Rng { + fn next(&mut self) -> u64 { + self.0 = self.0.wrapping_add(0x9e37_79b9_7f4a_7c15); + let mut z = self.0; + z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9); + z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb); + z ^ (z >> 31) + } + fn below(&mut self, n: usize) -> usize { + if n == 0 { 0 } else { (self.next() % n as u64) as usize } + } +} + +/// The values a length, count, size, index or bound is set to: the largest +/// it holds, and just past each bound the readers keep. +const BIG32: [u32; 9] = [u32::MAX, 0x7fff_ffff, 0x8000_0000, 1 << 16, (1 << 16) + 1, 1 << 20, (1 << 22) + 1, 1 << 24, 65_535]; +const BIG64: [u64; 6] = [u64::MAX, i64::MAX as u64, 1 << 32, (1 << 32) + 1, 0x7ff0_0000_0000_0000, 0x7ff8_0000_0000_0001]; + +/// A jeff module's words: each 8-byte word after the segment table. +fn jeff_mutation(rng: &mut Rng, b: &[u8], other: &[u8]) -> (String, Vec) { + let mut v = b.to_vec(); + let words = v.len() / 8; + match rng.below(9) { + 0 => { + let flips = 1 + rng.below(8); + for _ in 0..flips { + let i = rng.below(v.len().max(1)); + if let Some(x) = v.get_mut(i) { + *x ^= 1 << rng.below(8); + } + } + (format!("{flips} bits flipped"), v) + } + 1 => { + let n = rng.below(v.len()); + v.truncate(n); + (format!("cut to {n} bytes"), v) + } + 2 => { + let (i, j) = (rng.below(v.len()), rng.below(other.len())); + let mut s = v[..i].to_vec(); + s.extend_from_slice(&other[j..]); + (format!("the first {i} bytes and another file's from {j}"), s) + } + 3 => { + // The segment table: the count, or a segment's size. + let i = 4 * rng.below(3); + let x = BIG32[rng.below(BIG32.len())]; + if i + 4 <= v.len() { + v[i..i + 4].copy_from_slice(&x.to_le_bytes()); + } + (format!("segment-table word {} set to {x}", i / 4), v) + } + 4 | 5 => { + // A pointer: its offset, its struct's sizes, or its list's count. + let w = 1 + rng.below(words.saturating_sub(1).max(1)); + let at = 8 * w; + if at + 8 > v.len() { + return ("nothing".into(), v); + } + let mut p = u64::from_le_bytes(v[at..at + 8].try_into().unwrap()); + let what = match (p & 3, rng.below(3)) { + (0, 0) => { + p |= 0xffff_u64 << 32; + "a struct's data size at its largest" + } + (0, 1) => { + p |= 0xffff_u64 << 48; + "a struct's pointer count at its largest" + } + (1, 0) | (1, 1) => { + p = (p & 0x7_ffff_ffff) | (u64::from(BIG32[rng.below(BIG32.len())] >> 3) << 35); + "a list's count past its bounds" + } + _ => { + p = (p & !0xffff_fffc) | (u64::from(BIG32[rng.below(BIG32.len())]) & 0xffff_fffc); + "a pointer's offset past its bounds" + } + }; + v[at..at + 8].copy_from_slice(&p.to_le_bytes()); + (format!("word {w}: {what}"), v) + } + 6 | 7 => { + // A 32-bit field: a register's size, a loop's bound, an index, a length. + let i = 8 + 4 * rng.below(v.len().saturating_sub(8) / 4); + let x = BIG32[rng.below(BIG32.len())]; + if i + 4 <= v.len() { + v[i..i + 4].copy_from_slice(&x.to_le_bytes()); + } + (format!("the 32 bits at {i} set to {x}"), v) + } + _ => { + // A 64-bit field: a 64-bit constant, or an angle. + let i = 8 + 8 * rng.below(words.saturating_sub(1)); + let x = BIG64[rng.below(BIG64.len())]; + if i + 8 <= v.len() { + v[i..i + 8].copy_from_slice(&x.to_le_bytes()); + } + (format!("the 64 bits at {i} set to {x:#x}"), v) + } + } +} + +const QIR_NUMBERS: [&str; 10] = ["4294967295", "18446744073709551615", "65536", "65537", "99999999999999999999999", "-1", "1e308", "nan", "0x7FF8000000000001", "2147483648"]; + +/// A QIR module's text: its numbers, its lines and its branches. +fn qir_mutation(rng: &mut Rng, t: &str, other: &str) -> (String, Vec) { + let lines: Vec<&str> = t.lines().collect(); + match rng.below(7) { + 0 | 1 => { + // A number: an index, a count, a version, an angle. + let spans: Vec<(usize, usize)> = { + let b = t.as_bytes(); + let mut out = Vec::new(); + let mut i = 0; + while i < b.len() { + if b[i].is_ascii_digit() { + let s = i; + while i < b.len() && (b[i].is_ascii_alphanumeric() || b[i] == b'.') { + i += 1; + } + out.push((s, i)); + } else { + i += 1; + } + } + out + }; + if spans.is_empty() { + return ("nothing".into(), t.as_bytes().to_vec()); + } + let (s, e) = spans[rng.below(spans.len())]; + let x = QIR_NUMBERS[rng.below(QIR_NUMBERS.len())]; + (format!("the number at {s} set to {x}"), format!("{}{x}{}", &t[..s], &t[e..]).into_bytes()) + } + 2 => { + // A line repeated many times. + let i = rng.below(lines.len()); + let k = [2usize, 64, 4096, 70_000][rng.below(4)]; + let mut out: Vec<&str> = lines[..i].to_vec(); + for _ in 0..k { + out.push(lines[i]); + } + out.extend_from_slice(&lines[i..]); + (format!("line {} repeated {k} times", i + 1), out.join("\n").into_bytes()) + } + 3 => { + // A branch turned back to the first block, or to itself. + let labels: Vec<&str> = lines.iter().filter_map(|l| l.trim().strip_suffix(':').filter(|x| !x.contains(' '))).collect(); + let to = labels.get(rng.below(labels.len().max(1))).copied().unwrap_or("entry"); + let i = rng.below(lines.len()); + let mut out: Vec = lines.iter().map(|l| l.to_string()).collect(); + out.insert(i, format!(" br i1 true, label %{to}, label %{to}")); + (format!("a branch to %{to} at line {}", i + 1), out.join("\n").into_bytes()) + } + 4 => { + let n = rng.below(t.len()); + let n = (0..=n).rev().find(|&k| t.is_char_boundary(k)).unwrap_or(0); + (format!("cut to {n} bytes"), t[..n].as_bytes().to_vec()) + } + 5 => { + let (i, j) = (rng.below(lines.len()), rng.below(other.lines().count().max(1))); + let mut out: Vec<&str> = lines[..i].to_vec(); + out.extend(other.lines().skip(j)); + (format!("the first {i} lines and another file's from line {}", j + 1), out.join("\n").into_bytes()) + } + _ => { + let mut b = t.as_bytes().to_vec(); + let flips = 1 + rng.below(4); + for _ in 0..flips { + let i = rng.below(b.len()); + b[i] ^= 1 << rng.below(7); + } + (format!("{flips} bits flipped"), b) + } + } +} + +thread_local! { + static CURRENT: std::cell::RefCell = const { std::cell::RefCell::new(String::new()) }; +} + +#[test] +fn every_mutated_module_converts_or_is_refused_with_a_code() { + let bundle: Value = serde_json::from_str(BUNDLE).expect("bundle.json reads"); + let mut files: Vec<(String, Format, Vec)> = Vec::new(); + for c in bundle["cases"].as_array().expect("cases") { + let name = c["file"].as_str().unwrap().to_string(); + match c["format"].as_str() { + Some("jeff") => files.push((name, Format::Jeff, formats::decode_base64(c["base64"].as_str().unwrap()).unwrap())), + Some("qir") => files.push((name, Format::Qir, c["text"].as_str().unwrap().as_bytes().to_vec())), + _ => {} + } + } + assert!(files.len() >= 40, "{} files", files.len()); + // A panic says which input it was, even where a panic aborts. + let hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + CURRENT.with(|c| eprintln!("the input that panicked: {}", c.borrow())); + hook(info); + })); + let mut rng = Rng(SEED); + let (mut converted, mut refused, mut inputs) = (0usize, 0usize, 0usize); + let mut slowest: Vec<(f64, usize, String)> = Vec::new(); + let mut worst_peak = (0usize, String::new()); + let mut failures = Vec::new(); + for (k, (name, format, bytes)) in files.iter().enumerate() { + // Each file unchanged, then its mutations. + for m in 0..=PER_FILE { + let (what, input) = if m == 0 { + ("unchanged".to_string(), bytes.clone()) + } else { + let others: Vec<&(String, Format, Vec)> = files.iter().filter(|f| f.1 == *format).collect(); + let other = &others[rng.below(others.len())].2; + match format { + Format::Jeff => jeff_mutation(&mut rng, bytes, other), + _ => qir_mutation(&mut rng, std::str::from_utf8(bytes).unwrap(), std::str::from_utf8(other).unwrap()), + } + }; + let label = format!("{name} #{m}: {what}"); + CURRENT.with(|c| *c.borrow_mut() = label.clone()); + let before = LIVE.load(Ordering::Relaxed); + PEAK.store(before, Ordering::Relaxed); + let start = std::time::Instant::now(); + let out = formats::convert(*format, Format::Qasm, &input, None, true); + let secs = start.elapsed().as_secs_f64(); + let peak = PEAK.load(Ordering::Relaxed).saturating_sub(before); + inputs += 1; + match out { + Ok(_) => converted += 1, + Err(d) if wai_quantum::qasm::diag::lookup(d.code).is_some() => refused += 1, + Err(d) => failures.push(format!("{label}: refused without a code of the table: {d}")), + } + if secs > MAX_SECONDS { + failures.push(format!("{label}: took {secs:.2} s, more than {MAX_SECONDS} s")); + } + if peak > MAX_PEAK { + failures.push(format!("{label}: allocated {peak} bytes at its peak, more than {MAX_PEAK}")); + } + if peak > worst_peak.0 { + worst_peak = (peak, label.clone()); + } + slowest.push((secs, k, label)); + } + } + // Every 32-bit field of each small jeff module set to its largest and to + // one past the register's cap, so that each loop bound, register size, + // array length and index the module holds is inflated in turn. + let mut swept = 0usize; + for (k, (name, format, bytes)) in files.iter().enumerate() { + if *format != Format::Jeff || bytes.len() > SWEEP_BYTES { + continue; + } + for i in (8..bytes.len().saturating_sub(3)).step_by(4) { + for x in [u32::MAX, (1 << 16) + 1] { + let mut input = bytes.clone(); + input[i..i + 4].copy_from_slice(&x.to_le_bytes()); + let label = format!("{name}: the 32 bits at {i} set to {x}"); + CURRENT.with(|c| *c.borrow_mut() = label.clone()); + let before = LIVE.load(Ordering::Relaxed); + PEAK.store(before, Ordering::Relaxed); + let start = std::time::Instant::now(); + let out = formats::convert(*format, Format::Qasm, &input, None, true); + let secs = start.elapsed().as_secs_f64(); + let peak = PEAK.load(Ordering::Relaxed).saturating_sub(before); + inputs += 1; + swept += 1; + match out { + Ok(_) => converted += 1, + Err(d) if wai_quantum::qasm::diag::lookup(d.code).is_some() => refused += 1, + Err(d) => failures.push(format!("{label}: refused without a code of the table: {d}")), + } + if secs > MAX_SECONDS { + failures.push(format!("{label}: took {secs:.2} s, more than {MAX_SECONDS} s")); + } + if peak > MAX_PEAK { + failures.push(format!("{label}: allocated {peak} bytes at its peak, more than {MAX_PEAK}")); + } + if peak > worst_peak.0 { + worst_peak = (peak, label.clone()); + } + slowest.push((secs, k, label)); + } + } + } + slowest.sort_by(|a, b| b.0.total_cmp(&a.0)); + eprintln!( + "{inputs} inputs: {converted} converted, {refused} refused with a code; slowest {:.3} s ({}); largest peak {} bytes ({})", + slowest[0].0, slowest[0].2, worst_peak.0, worst_peak.1 + ); + if std::env::var_os("WAI_FORMATS_MUTATION_REPORT").is_some() { + for (s, _, l) in slowest.iter().take(15) { + eprintln!(" {s:.3} s {l}"); + } + } + assert_eq!(inputs - swept, files.len() * (PER_FILE + 1), "the count of inputs is pinned by the files and PER_FILE"); + eprintln!("{swept} of them every 32-bit field of the jeff modules of at most {SWEEP_BYTES} bytes, inflated"); + assert!(failures.is_empty(), "{} of {inputs} inputs:\n{}", failures.len(), failures.join("\n")); +} diff --git a/wai/wai-quantum/views/manifest.json b/wai/wai-quantum/views/manifest.json index 951273c3d..2dda39fad 100644 --- a/wai/wai-quantum/views/manifest.json +++ b/wai/wai-quantum/views/manifest.json @@ -4,19 +4,19 @@ "placeholder": "__WAI_QUANTUM_VIEW__", "limit_bytes": 1048576, "bytes": { - "page": 804708, - "script": 94216, + "page": 804920, + "script": 94428, "styles": 50804, "engine_wasm": 193526, "engine_wasm_base64": 258036, "engine_js": 401202 }, "sha256": { - "page": "9b412e847dde188381d790b7d50aaaee11985ea3c5ba61a2dcac46d0fe1ba170", - "script": "136d99215a1a8cc7a1302a107a222823272fe7371949ffa2816cbb4f9599776a", + "page": "3f0893586575dad6fa934ce6774141e45397d4120d044ed7353b3571415c698a", + "script": "47c5c3d1921b34a8f5fbf3e6d65f9436f8e6ed5f216615f4c0544e40847b3c02", "styles": "11ebcd4416c68e9eaf1156ca6f7c92b9a96744895ed13c377eba98fdd8dca9a6", - "engine_wasm": "8373ee77c3603419a70387e01ad7f906d4d93b4345b5674a7785dfb37e573c0a", - "engine_js": "d5ecf0d52b1295077c60af6411fdba0757d9f3b6861e0b51bc30b97d21307f5c" + "engine_wasm": "1620f0db3d5a48183a7ddf07759bd970ae28e39b0e53cd75df2ff87dc16d3ffb", + "engine_js": "b13874380b21d7285c11b04595e3f03b631ccf75ddfa3db70d6d103cfce1c99f" }, "sources": { "wai/quantum-studio/build-views.mjs": "da5b161829a990b3d1bb4c1b3ec7c8f254ff7b5078476156e504ae322bc4f010", @@ -32,7 +32,7 @@ "wai/quantum-studio/locales/en/estimate_view.json": "b20c94f96751eee548b011ea19f6ded9ecd8648cae32885b6b71b7d5e631b2b1", "wai/quantum-studio/locales/en/examples.json": "dd2f2f4aef033b00b3700c6eb7151c18dd1b43537ef0beeb17a8ffc069a564b2", "wai/quantum-studio/locales/en/flags.json": "d2beeef25cee8ec742d692e424ec54bdc821449733ae98b2882cc97711efffdd", - "wai/quantum-studio/locales/en/frame.json": "9a7b2174c2ed2ac10f0c9c8dd393df72a97ad70b2eb58072f6622321b6101e3e", + "wai/quantum-studio/locales/en/frame.json": "608565c19197c6210b37a917ed27bbb3dbf6338cbeff23eebae2219140a3650e", "wai/quantum-studio/locales/en/gallery.json": "5d0c2992e39ea0f82541e0491b46f4fd724085d95a988f8c15a1e3657d2eb149", "wai/quantum-studio/locales/en/generators.json": "85eb826cc6353cf12bc3fa45bbb8721210a311f145b9b46493c3e2bb44e7a3c2", "wai/quantum-studio/locales/en/hardware.json": "745786e872062468680655326362f49ae85dabae6ae9021145072dd4dbd7fbf1", diff --git a/wai/wai-quantum/views/wai-quantum-views.html b/wai/wai-quantum/views/wai-quantum-views.html index 785ff3d72..2d8ffdacf 100644 --- a/wai/wai-quantum/views/wai-quantum-views.html +++ b/wai/wai-quantum/views/wai-quantum-views.html @@ -11,10 +11,10 @@
- - diff --git a/wai/wai-web/src/quantum_engine.rs b/wai/wai-web/src/quantum_engine.rs index 9513ee879..3020d7659 100644 --- a/wai/wai-web/src/quantum_engine.rs +++ b/wai/wai-web/src/quantum_engine.rs @@ -204,6 +204,24 @@ pub fn engine_equivalent(a: &str, b: &str) -> String { equiv::equivalent(a, b).to_string() } +/// A program carried between formats, OpenQASM 3, QIR (LLVM text) and jeff +/// (as base64), and checked: what is written is read back and held to the +/// program by the equivalence ladder +/// ([`wai::engine::formats::convert_json`]). `request` is +/// `{"to", "program" | "program_base64", "from"?, "profile"?, "check"?}`; a +/// refusal is `{"error", "code", "codes", "outcome", "meaning"}`, its code the +/// reader's (Q700 to Q721) and the place in its message. The Studio opens a +/// QIR or jeff file through it. +#[wasm_bindgen] +pub fn engine_convert(request: &str) -> String { + let answer = serde_json::from_str::(request).map_err(|e| format!("the request is not JSON: {e}")).and_then(|r| wai::engine::formats::convert_json(&r)); + answer.map(|v| v.to_string()).unwrap_or_else(|e| { + let mut r = refusal::refusal_json(&e); + r["error"] = json!(e); + r.to_string() + }) +} + /// The variants a program's fixes make, each checked equivalent to it /// ([`critique::variants`]). #[wasm_bindgen] @@ -1124,6 +1142,30 @@ mod tests { assert_eq!(body["result"]["counts"], json!({ "00": 50 }), "{body}"); } + /// The QIR the conformance set pins for its Clifford+T program + /// (`wai/quantum-conformance/formats/qasm/clifford_t.qasm`), which every + /// build writes to the same bytes. + pub(super) const CLIFFORD_T: &str = include_str!("../../quantum-conformance/formats/qasm/clifford_t.qasm"); + pub(super) const CLIFFORD_T_QIR: &str = "967659b446471c62697f49df341972a45f47bef59d3b2fe26bfefc6670f6a50d"; + + /// A program carried between every pair of formats in the page, each + /// conversion checked, and a refusal with its code and line. + #[test] + fn a_program_converts_between_formats_in_the_page() { + let qir: Value = serde_json::from_str(&engine_convert(&json!({ "program": CLIFFORD_T, "to": "qir" }).to_string())).unwrap(); + assert_eq!((qir["written"]["profile"].as_str(), qir["check"]["verdict"]["verdict"].as_str()), (Some("base"), Some("equivalent")), "{qir}"); + assert_eq!(blake3::hash(qir["program"].as_str().unwrap().as_bytes()).to_hex().as_str(), CLIFFORD_T_QIR); + let jeff: Value = serde_json::from_str(&engine_convert(&json!({ "program": qir["program"], "to": "jeff" }).to_string())).unwrap(); + assert_eq!((jeff["from"].as_str(), jeff["check"]["verdict"]["verdict"].as_str()), (Some("qir"), Some("equivalent")), "{jeff}"); + let back: Value = serde_json::from_str(&engine_convert(&json!({ "program_base64": jeff["program_base64"], "to": "qasm" }).to_string())).unwrap(); + assert_eq!(back["from"], "jeff", "{back}"); + let e: Value = serde_json::from_str(&engine_equivalent(back["program"].as_str().unwrap(), CLIFFORD_T)).unwrap(); + assert_eq!(e["verdict"], "equivalent", "{e}"); + let r: Value = serde_json::from_str(&engine_convert(&json!({ "program": "define void @main() {\nentry:\n call void @__quantum__qis__foo__body(ptr null)\n ret void\n}\n", "from": "qir", "to": "qasm" }).to_string())).unwrap(); + assert_eq!(r["code"], "Q706", "{r}"); + assert!(r["error"].as_str().unwrap().starts_with("line 3: "), "{r}"); + } + #[test] fn critique_equivalence_and_variants_answer_as_json() { let src = "qubit[2] q;\nh q[0];\nh q[0];\ncx q[0], q[1];\n"; @@ -1261,6 +1303,17 @@ mod wasm_tests { assert_eq!(v["valid"], true, "{v}"); } + /// The conversion the native test makes, to the same QIR bytes. + #[wasm_bindgen_test] + fn a_program_converts_between_formats_in_wasm_to_the_native_bytes() { + use super::tests::{CLIFFORD_T, CLIFFORD_T_QIR}; + let qir: Value = serde_json::from_str(&engine_convert(&json!({ "program": CLIFFORD_T, "to": "qir" }).to_string())).unwrap(); + assert_eq!(qir["check"]["verdict"]["verdict"], "equivalent", "{qir}"); + assert_eq!(blake3::hash(qir["program"].as_str().unwrap().as_bytes()).to_hex().as_str(), CLIFFORD_T_QIR); + let jeff: Value = serde_json::from_str(&engine_convert(&json!({ "program": qir["program"], "to": "jeff" }).to_string())).unwrap(); + assert_eq!(jeff["check"]["verdict"]["verdict"], "equivalent", "{jeff}"); + } + #[wasm_bindgen_test] fn critique_equivalence_and_variants_run_in_wasm() { let c: Value = serde_json::from_str(&engine_critique("qubit[2] q; h q[0]; rz(0.3) q[1]; cx q[0], q[1];")).unwrap();