diff --git a/.github/wai-conformance/verifiers/quantum-receipts.sh b/.github/wai-conformance/verifiers/quantum-receipts.sh new file mode 100644 index 000000000..7c90642c2 --- /dev/null +++ b/.github/wai-conformance/verifiers/quantum-receipts.sh @@ -0,0 +1,21 @@ +# step: quantum job receipts · receipt_verify.py +# order: 701 +# paths: wai/extensions/quantum-providers.md +# working-directory: . +# +# Job receipts (wai.quantum.provider/1 section 8, wai/quantum-conformance/receipts/): the +# corpus's crafted half is what generate.py writes; receipt_verify.py, written in Python from +# section 8 alone and sharing no code with the engine (its BLAKE3, Ed25519, RFC 8785 text and +# strict reader its own, standard library only), passes its self-test against published +# answers and the number texts a JavaScript engine wrote, and gives every receipt of the +# corpus exactly the reasons the manifest gives; and each of its planted defects makes that +# fail. The reference's half of the differential, `wai-quantum verify` on every receipt and +# on every one-member change of each valid one, runs in wai-quantum-cli.yml beside the build +# of the binary. Run isolated (-I). +python3 -I wai/quantum-conformance/receipts/generate.py --check +python3 -I wai/quantum-conformance/receipts/generate.py --self-test +python3 -I wai/quantum-conformance/tools/receipt_verify.py --self-test wai/wai-quantum/src/engine/testdata/es-number-tostring.txt +python3 -I wai/quantum-conformance/tools/receipt_differential.py --second-only +for plant in lenient-key no-alg last-duplicate no-inexact measured-simulator whole-cap lax-small-order-r no-canonical-spelling set-seal-only names-layout-only; do + python3 -I wai/quantum-conformance/tools/receipt_differential.py --second-only --plant "$plant" | tail -1 +done diff --git a/.github/workflows/wai-quantum-cli.yml b/.github/workflows/wai-quantum-cli.yml index 8c2bc37e3..b6298679d 100644 --- a/.github/workflows/wai-quantum-cli.yml +++ b/.github/workflows/wai-quantum-cli.yml @@ -8,7 +8,10 @@ name: wai-quantum command line # include the model harness (src/eval.rs): every task's reference solved and an # empty program not, a scripted run, a model server on this machine asked and one # elsewhere refused, and the corpus (wai/quantum-conformance/eval/) held to the -# task set. +# task set. And job receipts by two verifiers (wai/quantum-conformance/receipts/): +# `verify` and receipt_verify.py, written from the provider protocol's section 8 +# alone, each held to the corpus's manifest and to the other on every receipt and +# every one-member change of each valid one, and each planted defect caught. concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -22,6 +25,11 @@ on: - 'wai/wai-quantum-cli/**' - 'wai/wai-quantum-qpu/**' - 'wai/quantum-conformance/eval/**' + - 'wai/quantum-conformance/receipts/**' + - 'wai/quantum-conformance/tools/receipt_verify.py' + - 'wai/quantum-conformance/tools/receipt_differential.py' + - 'wai/quantum-conformance/tools/referee.py' + - 'wai/extensions/quantum-providers.md' - 'rust-toolchain.toml' - '.github/workflows/wai-quantum-cli.yml' pull_request: @@ -30,6 +38,11 @@ on: - 'wai/wai-quantum-cli/**' - 'wai/wai-quantum-qpu/**' - 'wai/quantum-conformance/eval/**' + - 'wai/quantum-conformance/receipts/**' + - 'wai/quantum-conformance/tools/receipt_verify.py' + - 'wai/quantum-conformance/tools/receipt_differential.py' + - 'wai/quantum-conformance/tools/referee.py' + - 'wai/extensions/quantum-providers.md' - 'rust-toolchain.toml' - '.github/workflows/wai-quantum-cli.yml' @@ -80,7 +93,13 @@ jobs: # Your own providers end to end (tests/providers.rs), against the # reference provider this binary serves, and their jobs as MCP # tasks, each test by name. - for t in a_person_adds_tests_plans_and_runs_on_a_provider_and_the_receipt_verifies \ + for t in json_output_does_not_skip_the_history_it_was_asked_to_check \ + lines_judge_a_set_as_a_set \ + the_mcp_tool_given_a_job_s_text_gives_the_corpus_s_verdict \ + a_member_named_twice_is_unreadable_on_every_kind_of_job \ + the_mcp_tool_gives_the_corpus_s_verdict_on_what_a_parsed_job_shows \ + a_named_credential_that_is_empty_never_falls_to_the_account_s_keys \ + a_person_adds_tests_plans_and_runs_on_a_provider_and_the_receipt_verifies \ a_run_that_is_not_free_is_refused_until_the_person_confirms_it \ a_credential_is_kept_in_a_file_only_its_owner_reads_and_appears_in_nothing_printed \ a_provider_over_http_runs_with_its_credential_in_the_header \ @@ -110,6 +129,31 @@ jobs: set -euo pipefail cargo build --locked --release bash smoke.sh ./target/release/wai-quantum + # Job receipts, judged by two verifiers that share no code: this binary's `verify`, and + # receipt_verify.py, written in Python from wai/extensions/quantum-providers.md section 8 + # alone (standard library only, its BLAKE3 and Ed25519 its own). The corpus's crafted half + # is what generate.py writes; the second verifier's self-test holds its BLAKE3, Ed25519, + # RFC 8785 and number text to published answers. Then both verifiers and the manifest must + # agree on every receipt of the corpus, and the two verifiers on every receipt made from a + # valid one by changing one member, or by setting a number past a double's whole numbers. + # Each planted defect of the second verifier (a lenient key check, alg unchecked, the last + # of a duplicate member kept, inexact numbers let through, a simulator's counts let pass as + # measured, a ceiling on every whole number, R of small order let through, the canonical + # spelling of a large double refused, a set's seal checked and nothing of its items, + # qubit names held to the layouts only) must make the + # differential fail: the proof that it can. A plant that the corpus alone catches is caught + # by the second-only run too, in the independent verifiers' step. + - name: Job receipts, two verifiers + shell: bash + run: | + set -euo pipefail + python3 -I wai/quantum-conformance/receipts/generate.py --check + python3 -I wai/quantum-conformance/receipts/generate.py --self-test + python3 -I wai/quantum-conformance/tools/receipt_verify.py --self-test wai/wai-quantum/src/engine/testdata/es-number-tostring.txt + python3 -I wai/quantum-conformance/tools/receipt_differential.py --reference wai/wai-quantum-cli/target/release/wai-quantum + for plant in lenient-key no-alg last-duplicate no-inexact measured-simulator whole-cap lax-small-order-r no-canonical-spelling set-seal-only names-layout-only; do + python3 -I wai/quantum-conformance/tools/receipt_differential.py --reference wai/wai-quantum-cli/target/release/wai-quantum --plant "$plant" | tail -1 + done # The release binary, pinned by version and SHA-256, rather than a build from source. # Every WAI workflow that runs wasmtime pins this one version, and every run passes # -W nan-canonicalization=y -W relaxed-simd-deterministic=y: every NaN a float diff --git a/.github/workflows/wai-quantum-contract.yml b/.github/workflows/wai-quantum-contract.yml index de34d70d4..46403bfbd 100644 --- a/.github/workflows/wai-quantum-contract.yml +++ b/.github/workflows/wai-quantum-contract.yml @@ -136,7 +136,11 @@ jobs: set -euo pipefail for pair in "wai-quantum $WAI_QUANTUM_BASELINE" "wai-quantum-cli $WAI_QUANTUM_CLI_BASELINE" "wai-quantum-qpu $WAI_QUANTUM_QPU_BASELINE"; do set -- $pair - latest=$(cargo info "$1" 2>/dev/null | sed -n 's/^version: *\([^ ]*\).*/\1/p' | head -1 || true) + # `cargo info` names the version it would select and, when that is not the newest, + # the newest after it: `version: 0.4.0 (latest 0.4.1)`. The last published is the latter. + line=$(cargo info "$1" 2>/dev/null | sed -n 's/^version: *//p' | head -1 || true) + latest=$(printf '%s\n' "$line" | sed -n 's/.*(latest \([^)]*\)).*/\1/p') + [ -n "$latest" ] || latest=${line%% *} if [ -z "$latest" ]; then echo "::warning::crates.io did not say which version of $1 is the last; the check runs against $2" elif [ "$latest" != "$2" ]; then diff --git a/.github/workflows/wai-quantum-py.yml b/.github/workflows/wai-quantum-py.yml index d3d834e53..1f5fd37be 100644 --- a/.github/workflows/wai-quantum-py.yml +++ b/.github/workflows/wai-quantum-py.yml @@ -18,6 +18,7 @@ on: push: branches: [main] paths: + - 'wai/quantum-conformance/receipts/**' - 'wai/wai-quantum/**' - 'wai/wai-quantum-py/**' - 'wai/wai-quantum-cli/**' @@ -26,6 +27,7 @@ on: - '.github/workflows/wai-quantum-py.yml' pull_request: paths: + - 'wai/quantum-conformance/receipts/**' - 'wai/wai-quantum/**' - 'wai/wai-quantum-py/**' - 'wai/wai-quantum-cli/**' diff --git a/.github/workflows/wai-quantum-qpu.yml b/.github/workflows/wai-quantum-qpu.yml index 60e65e157..ef38d0b5e 100644 --- a/.github/workflows/wai-quantum-qpu.yml +++ b/.github/workflows/wai-quantum-qpu.yml @@ -79,9 +79,10 @@ jobs: # tests never touch one but the macOS leg's own. export WAI_QUANTUM_CREDENTIALS=file cargo test --locked 2>&1 | tee "$RUNNER_TEMP/test.log" - # The library (46), the clients against their contracts (8), and the - # providers end to end (12), each binary's count pinned. - for n in 46 8 12; do + # The library (81, the cloud-tasks client's 26 among them), the clients + # against their contracts (15), and the providers end to end (12), each + # binary's count pinned. + for n in 81 15 12; do grep -qE "^test result: ok\. $n passed; 0 failed" "$RUNNER_TEMP/test.log" || { echo "::error::a test binary did not pass all $n of its tests"; exit 1; } done for t in curl_carries_a_request_to_a_server_and_back every_operation_over_http every_operation_over_a_local_process \ @@ -90,7 +91,25 @@ jobs: a_credential_reaches_the_provider_and_nothing_a_run_returns \ job::tests::a_recorded_job_outlives_what_sent_it_and_seals_as_a_run_would job::tests::running_out_of_patience_does_not_cancel_the_job \ job::tests::nothing_is_sent_that_could_not_be_recorded_first job::tests::a_job_that_may_have_been_sent_is_never_sent_again \ - ledger::tests::a_handle_cannot_reach_outside_the_records; do + ledger::tests::a_handle_cannot_reach_outside_the_records \ + cloud_tasks::tests::the_signature_is_the_published_example cloud_tasks::tests::a_processor_gets_a_verbatim_box_on_its_own_qubit_names \ + cloud_tasks::tests::a_submission_names_the_persons_own_bucket_and_never_carries_their_secret \ + job::tests::what_a_provider_states_of_its_counts_is_sealed_and_a_statement_out_of_form_is_refused \ + job::tests::a_job_recorded_before_clients_stated_what_they_sent_says_no_one_stated_it \ + every_built_in_kind_states_what_it_sent_and_its_receipt_keeps_every_rule \ + cloud_tasks::tests::temporary_keys_send_their_token_and_sign_it cloud_tasks::tests::the_look_for_a_secret_reads_a_request_s_body \ + cloud_tasks::tests::a_program_on_a_qubit_past_the_sixty_fourth_is_read_back cloud_tasks::tests::a_dotted_bucket_is_read_by_path_where_its_host_has_no_certificate \ + registry::tests::a_named_credential_that_gives_nothing_is_an_error_saying_where_it_looked \ + registry::tests::every_part_of_kept_keys_is_taken_out_of_a_message_whole_or_cut_short \ + sampler_v2::tests::a_simulated_backend_is_not_silicon \ + a_job_recorded_by_the_last_release_is_sealed_with_a_receipt_this_one_accepts \ + a_receipt_names_the_device_s_qubits_as_the_program_was_sent_on_them \ + cloud_tasks::tests::signing_gives_the_published_test_suite_s_signatures \ + cloud_tasks::tests::a_client_that_did_not_submit_a_task_does_not_read_it_through_today_s_device \ + cloud_tasks::tests::a_task_is_asked_after_in_the_region_its_id_names \ + registry::tests::a_kind_this_build_does_not_know_is_kept_and_the_rest_is_read \ + a_cloud_task_cancelled_while_it_waits_ends_cancelled \ + cloud_tasks_round_trip_through_their_contract a_job_collected_later_is_read_through_the_qubits_it_was_sent_on; do grep -q "^test $t \.\.\. ok$" "$RUNNER_TEMP/test.log" || { echo "::error::$t did not run and pass"; exit 1; } done - name: Clippy diff --git a/.github/workflows/wai-quantum-service.yml b/.github/workflows/wai-quantum-service.yml index 05e707fc9..d060ff04a 100644 --- a/.github/workflows/wai-quantum-service.yml +++ b/.github/workflows/wai-quantum-service.yml @@ -17,6 +17,7 @@ on: push: branches: [main] paths: + - 'wai/quantum-conformance/receipts/**' - 'wai/wai-quantum/**' - 'wai/wai-quantum-service/**' # The command line's agent docs list this service's tools from a snapshot @@ -26,6 +27,7 @@ on: - '.github/workflows/wai-quantum-service.yml' pull_request: paths: + - 'wai/quantum-conformance/receipts/**' - 'wai/wai-quantum/**' - 'wai/wai-quantum-service/**' # The command line's agent docs list this service's tools from a snapshot @@ -77,6 +79,7 @@ jobs: set -euo pipefail cargo test --locked --release 2>&1 | tee "$RUNNER_TEMP/test.log" grep -qE '^test result: ok\. [1-9][0-9]* passed; 0 failed' "$RUNNER_TEMP/test.log" || { echo "::error::no test ran"; exit 1; } + grep -q '^test the_service_gives_every_receipt_the_verdict_the_corpus_gives_it \.\.\. ok$' "$RUNNER_TEMP/test.log" || { echo "::error::the service was not held to the corpus of receipts"; exit 1; } cargo clippy --locked --release --all-targets -- -D warnings # The engine's parts of the service, each required by name: sets and # sweeps and their Merkle paths, a result's provenance, the package diff --git a/.github/workflows/wai-quantum-studio.yml b/.github/workflows/wai-quantum-studio.yml index 80a1866b9..277b4e4fb 100644 --- a/.github/workflows/wai-quantum-studio.yml +++ b/.github/workflows/wai-quantum-studio.yml @@ -9,6 +9,8 @@ name: wai-quantum studio and desktop # history kept across a reload; a device target's coupling map coloured by its # errors, a run placed and simulated on it, and the layout drawn on the map; # nothing scrolls sideways at a phone's width; +# - test/verify-text.test.mjs, without a browser: a receipt is checked on the text it +# came in, not an object written out again. # - test/hardware.test.mjs, without a browser: "Your hardware" asks the person before # a run that is not free, never before a free one, and sends nothing on a "no"; a job # in the provider's queue is asked after by its id, Cancel cancels it there, "Wait in @@ -134,6 +136,7 @@ jobs: npm ci --no-audit --no-fund npx playwright install --with-deps chromium node test/hardware.test.mjs + node test/verify-text.test.mjs node test/service.test.mjs node test/telemetry.test.mjs node test/update.test.mjs diff --git a/.github/workflows/wai-web-wasm.yml b/.github/workflows/wai-web-wasm.yml index 8c9fe61c5..67f28452c 100644 --- a/.github/workflows/wai-web-wasm.yml +++ b/.github/workflows/wai-web-wasm.yml @@ -264,7 +264,7 @@ jobs: 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. 29 passed; 0 failed" "$RUNNER_TEMP/wai-web-engine.log" + grep -q "test result: ok. 30 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/changes/2026-11-05-provider-receipts-state-counts-execution-second-verifier.md b/wai/changes/2026-11-05-provider-receipts-state-counts-execution-second-verifier.md new file mode 100644 index 000000000..02e246202 --- /dev/null +++ b/wai/changes/2026-11-05-provider-receipts-state-counts-execution-second-verifier.md @@ -0,0 +1,91 @@ +### wai-quantum: a provider's receipt states where its counts come from, what was set at the provider, and a simulator's counts as simulated; and a second verifier, written from the text alone, checks every receipt + +- **Where the counts come from** (`counts_source`, [extensions/quantum-providers.md](extensions/quantum-providers.md) + §8.3): `shots` (tallied from one record a shot), `vendor_counts` (the provider's own) or `probabilities` + (rebuilt by largest remainders: derived, not shot records). `native-turns` always rebuilds them; + `cloud-tasks` reads a task's rows and rebuilds only when its result has none; `sampler-v2` tallies shots; + `prx-circuits` takes the service's counts. +- **What was set at the provider** (`execution`, §8.3): each of mitigation, twirling, decoupling, remapping and + optimisation `off`, `on`, or `default` when the request sets nothing for it, whether the gates were to run + as written, and what the client sent (the body's members and the settings among them). Each built-in client + builds it from the very body it posts (`Provider::execution`), and a test holds every one to the body its + scripted service received: `sampler-v2` turns decoupling and twirling off, `native-turns` debiasing, + `cloud-tasks` puts a processor's gates in the service's verbatim box, and `prx-circuits` sends only + `circuits` and `shots`, so it sets nothing and says so. A provider of the open protocol may state both in its + `result` answer (§5.5); one that wraps another service must. +- **A simulator's counts are `simulated`, not `measured`.** The class follows the provider's provenance: + `measured` for `silicon`, `simulated` for a simulator or an emulator, with `approximate: true` when the + provider's `describe` says its simulator is approximate. The note no longer claims "no mitigation", which no + client turned off or recorded. A receipt sealed before (the earlier form) verifies as it did, and a verifier + reads its class by its provenance and says so, without claiming when it was sealed: nothing in it says. A receipt from a person's account names that account, so the + corpus holds none; the two `cloud-tasks` runs on a cloud service's simulator, sealed as `measured` on 5 November, + were checked by hand with the differential's `--extra`, and verify and read as simulated. +- **A job recorded by the last release, and collected by this one, gets a receipt this one accepts.** Its record has + its program, so its text is added, and no one stated what was sent, so its `execution` says `nobody`, which every + kind takes. +- **The receipt says which qubits.** `compiled.qasm3` is on the coupling map's indices, and `compiled.qubit_names` + gives the device's own name for each, as it was when the job was sent, where a client sends it under them + (`cloud-tasks`, `prx-circuits`). +- **`sampler-v2` reads a backend's `simulator`:** a simulated backend's counts are `simulated`, not `measured`. +- **The compiled program's text** is in the receipt (`compiled.qasm3`), beside its hash, so anyone can read + the program the device was sent and check the one against the other. +- **The receipt's text is §8 of the protocol**: what a provider's result carries, the seal byte for byte, + the strict reader, the canonical text and the first form's, the strict Ed25519 rules, the per-kind rows, + the seventeen reasons a verifier gives, and what a receipt proves. `wai_quantum::engine::job::check` + (and `check_text`, `check_bytes`) gives them, and every surface's `verify` goes through it: `POST /v1/verify` and + the service's tool, which until now checked the seal alone, the command line's tool, its `verify` on a set, an + estimate or an algorithm's answer, the Python package, the desktop app, the browser and `job::verify`. A surface + given a job's text reads it as text, so a member named twice is found. Each answers with `reasons` beside `valid` + and `key`, and each set item is held to the rules of a job. +- **What a host seals, its verifier accepts.** `qec` on the command line, the desktop, the browser and Python took a + seed of any size and sealed the double, and `verify` now refuses that text (`inexact_number`). A compile, a pulse, + an estimate, `@Seal` and a project's intent check took such a number too. Each now refuses the request where it reads + it, as a run has, and a whole number no double holds in a result is written as the double sealed + (`receipt::seal_into`). **A receipt already sealed with one stays refused** (only `qec`'s, given a seed past 2^53): + its signature holds, what it sealed is the double, and written as that double's digits it verifies. + `wai-quantum-qec` seeds past 2^53 − 1 are a refusal now, not a rounding. VERSIONING.md says so. +- **A whole double past 2^53 verifies as RFC 8785 writes it.** §8.2 promises that a result verifies after any JSON + reader and writer has carried it, and RFC 8785 (and JavaScript) write 2^60 as 1152921504606847000, its shortest + digits padded with zeros, which `inexact_number` refused. That spelling, the double's own, is now taken; every other + spelling of an inexact whole number is still refused. A host screens every number of a request it seals, as it + writes it and as RFC 8785 does, so it seals nothing its verifier refuses. +- **§8.6: a set's receipt.** A set (`wai.quantum.set/1`) is now part of §8: its receipt seals its request and its head; + each item that ran is a job of its own; its items are the ones its request gives; its root is the Merkle root of the + items' leaves, and each item's path leads to it. Three reasons: `set_items`, `set_item`, `set_root`. Every surface + judges a set by it, the second verifier included, and the corpus holds two sets the command line made and eleven + crafted to break one rule each. Python's text and path inputs and `verify --lines` read a genuine set as a plain job + and refused every one: they no longer do. A set's own request, its sweep's values among them, is screened as a run's. + An item is read for its `index`, `request`, `result`, `error` and `path`; the rest of it is the host's, unsealed. + An item's receipt names the set's key; a failed item writes no number its leaf (the canonical form's digest) + could not seal; an empty set, or an item that is itself a set, is refused. +- **A receipt is checked on its text.** The Studio, the desktop app and the npm package's example verified an object + written out again by JavaScript, which moves a member named by digits ahead of the others and rewrites a large whole + number, so a genuine first-form receipt failed. They pass the text they were given; an object is written out only + when it is all there is. The tools of the command line and the job service take a job's text (`job`), so they see + what parsing loses, and their tests hold them to the whole corpus, none skipped. +- **`compiled.qubit_names` names every qubit the text acts on**, each `$` and its digits in `qasm3`, not only those of + the layouts: routing can pass through a qubit in neither. +- **A whole number has no ceiling but a count's.** The reference capped every whole number at 2^53 − 1 where §8.4 caps + only a count's value, and read equal strings as different shots; §8.4 was right, and it follows it now, with sums + exact. The differential could not see this, since it changed a number only by adding to it: it now tries values past + a double's whole numbers, and shots in both request and result, and a plant that restores the ceiling proves it can. +- **`verify --json --history` checks the history**, as the text does, and `verify --json` on a set, an estimate or an + algorithm's answer gives JSON. `--history` on one of those is refused, rather than ignored. +- **The reference was too lenient, and is not now.** It accepted, as valid: a receipt under the identity as + its key, for any content (a library's default Ed25519 check takes a small-order key); `alg` other than + `ed25519`; `canon: null`, read as the first form; hex in upper case or with `+` (a second spelling of an + unsealed receipt); and a member named twice. A key with a two-byte character among its hex digits made + `wai-quantum verify` panic. It now refuses each, decodes keys and `R` canonically, refuses `S ≥ ℓ` itself + whatever the library's features, and keeps a first-form result's member order when it removes the receipt. +- **`wai-quantum verify --json`** gives a job's verdict as data (valid, the reasons, the key, the form, and + what a provider's result says of itself), and **`--lines`** judges one job a line. Its text names the + rules a receipt breaks and, for a provider's result, its class, its counts' source and its `execution`. +- **A second verifier** (`quantum-conformance/tools/receipt_verify.py`), written in Python from §8 alone by a + reader who had not seen the reference, standard library only (its BLAKE3, Ed25519, RFC 8785 text and strict + reader its own). Its readings of the text, gap by gap, became §8's sentences. A corpus + (`quantum-conformance/receipts/`): receipts made by the command line, the job service and every built-in + kind through a scripted service, the engine's fixtures, and receipts crafted to break one rule each, every + expectation written by hand. `receipt_differential.py` holds the two verifiers and the manifest to each + other on every receipt and on every one-member change of each valid one, and each defect planted in the + second verifier must be caught; CI runs it beside the build of the command line, and the second verifier + against the manifest among the independent verifiers. diff --git a/wai/changes/2026-11-05-qpu-built-in-provider-kind-cloud-tasks.md b/wai/changes/2026-11-05-qpu-built-in-provider-kind-cloud-tasks.md new file mode 100644 index 000000000..b7d302f5a --- /dev/null +++ b/wai/changes/2026-11-05-qpu-built-in-provider-kind-cloud-tasks.md @@ -0,0 +1,65 @@ +### wai-quantum-qpu: a built-in provider kind for a cloud service of quantum tasks + +- **`cloud-tasks`** ([wai-quantum-qpu/src/cloud_tasks.rs](wai-quantum-qpu/src/cloud_tasks.rs), the fourth built-in + protocol and the sixth kind in [bring-your-own-quantum.md](wai-quantum-qpu/bring-your-own-quantum.md)). `wai-quantum + providers add NAME --kind cloud-tasks --set region=… --set device=… --set bucket=… --credential-stdin` connects one + device on the person's own account. Requests are signed with their keys (Signature Version 4), so the secret never + crosses the wire, and results land in their own storage bucket. +- **The program runs as compiled.** On a processor it is OpenQASM 3 in a verbatim box on physical qubits, with every + angle an exact multiple of pi. A device whose native gates are `rx rz cz` takes the `rzsxxcz` basis, with `sx` and `x` + written as `rx(pi/2)` and `rx(pi)`: they differ only by a global phase, which no count can see. A device whose native + gates fit no basis is refused, with what it runs in the message, rather than compiled by someone else. +- **On a processor the estimate is the whole charge; on a simulator, the least it can be.** The service's API states + only the device's price per shot. The estimate adds the service's fee per task (0.30 USD, from its price list of + 2026-11-05) and says where each part comes from. A simulator is billed by the minute with a three-second minimum, and + how long a task takes is not known before it runs, so its estimate is that minimum: `Estimate::at_least`, and + `at_least: true` in a plan and a receipt, and every surface says "at least". +- **A job is read through the qubits it was sent on.** An index of the device's coupling map means what the device's + list said on the day, and that list can change while a job waits. `Provider::qubit_names` gives the device's own names + for a program's qubits, a job's record keeps them beside its program, and `Provider::counts_stated` reads through + them, without asking the device again. A client with no record of a job on a processor refuses rather than guess. +- **A paid result can be read.** A qubit past the sixty-fourth can be read back: logical qubits are read from the row by + label, with no packing into a 64-bit word. A qubit the program ended on that was not measured is an error, not a zero. +- **Nothing is sent to a name that is not one.** `region` and `bucket` go into hosts that get signed requests, so + `CloudTasks::new` refuses what could not be one, as `providers add` does. A bucket with a dot is legal, and is read by + path, since the storage service's certificate covers one label; results are read from the bucket the task names, + whatever the entry names by now. This kind reaches two hosts and takes no `--endpoint`: it refuses one rather than + listing an address it would not use. +- **A named credential that gives nothing is an error.** A built-in kind falls back to its environment variables only + when the entry names no credential, here and in the three older kinds. With one named and empty, it says where it + looked, and signs with nothing else. +- **Every part of the keys is taken out of a message.** `Target::with_secrets`, and `Secret::redact` removes what a cut + at a length leaves of a value too. +- **`wai-qpu probe` and `certify` ask for `--yes` wherever a job may spend:** on a processor, as `certify` did, and on + anything the service does not state is free (a price stated, or none). `probe` asked for nothing before. +- **A task is asked after where it is.** Its region is read from its id, and its results from the bucket it names, in + that region, whatever the provider's entry names by now. A client reads a processor's task through the qubits it + read only when it submitted that task itself; otherwise through the job's record, or not at all. +- **`providers test`** says "at least" of a simulator's minimum, in what it ran and in why it ran nothing. +- **Signing is held to the published test suite** (its v4 vectors): paths with spaces, UTF-8 and every unreserved + character, by the task service's rule and by storage's, and a request with a body. The fake service the client is + tested against reads the bucket from the host or, for a name with a dot, from the path; walks a task through + queued, running and completed, or cancelling and cancelled; and writes results in the published result schema's + form (gate-model task result v1: `measuredQubits` the device's qubit numbers, as integers). +- **`CloudTasks::with_endpoints` is `CloudTasks::with_base`**: a test server for the task service only. Results are + always read from the storage service's own hosts, as the bucket's name addresses them. +- **A list of providers holding a kind this build does not know** is read: that entry is left as it is, noted, kept + when the list is saved, and not used. **A list holding a `cloud-tasks` entry cannot be read by an earlier build** + (`wai-quantum-qpu` 0.4.1 and before, `wai-quantum-cli` 0.2.4 and before, and the desktop app built on them): they + refuse the whole file, every provider in it. That cannot be fixed in the releases already out; a person who shares + one list between versions keeps a `cloud-tasks` entry out of it until every one of them is this version or later. +- **A simulator can run a processor's gates.** `--set basis=gpizz` on one of the service's simulators compiles to that + basis, so the gates can be checked before paying for a processor. A processor refuses the setting. +- **`Method::Put`** and `Request::put` in the crate's transport, for cancelling a task. +- **The crate is 0.5.0, since its Rust API breaks** (`wai-quantum/VERSIONING.md`: before 1.0 a breaking change raises + the 0.y component). `registry::Kind` gains `CloudTasks`, before `Http` and `Process`, whose discriminants move; and + `transport::Method` gains `Put`. Both were exhaustive, so a `match` on either without a wildcard arm no longer + compiles, and a cast of `Kind::Http` or `Kind::Process` to a number gives another value. What to do instead: + - give a `match` on `Kind` or on `Method` a wildcard arm. Both are `#[non_exhaustive]` from this version: a kind and a + method are each a value of a list that grows, which a minor release may add to, and with the enums exhaustive every + new kind would have been a breaking release. Inside the crate a `match` stays exhaustive, so a kind added there + without its name, its settings and its client still fails to build; + - read `Kind::ALL` as a slice (`Kind::ALL.iter()`): it was an array, `[Kind; 5]`, whose type changed with every kind; + - name a kind by `Kind::name()` and read one with `Kind::parse`, never by its number. + + `wai-quantum-cli` and the desktop app require 0.5.0. Neither's own API changes. diff --git a/wai/changes/2026-11-05-studio-code-shown-in-one-step.md b/wai/changes/2026-11-05-studio-code-shown-in-one-step.md new file mode 100644 index 000000000..9a7c95702 --- /dev/null +++ b/wai/changes/2026-11-05-studio-code-shown-in-one-step.md @@ -0,0 +1,9 @@ +### quantum-studio: a code's summary, certificate and lattice change in one step + +- **The error-correction pane** ([quantum-studio/src/qec-view.js](quantum-studio/src/qec-view.js)) showed a newly chosen + code's summary and decoders while its seal was still being checked, over the lattice and the certificate of the code + chosen before; a qubit clicked in that moment was flipped in the new code. The seal is now checked first, and the + summary, the decoders, the certificate and the lattice change together, as a decode's already did. A threshold drew + its plot before the check and wrote its reading after; they change together too. The browser test read the lattice + as soon as the summary named the new code, and once saw the surface code's two faces where it expected the colour + code's nine; it is unchanged, and passes with the seal's check slowed, where it failed before. diff --git a/wai/extensions/quantum-providers.md b/wai/extensions/quantum-providers.md index 671626d3b..d6ac9e52b 100644 --- a/wai/extensions/quantum-providers.md +++ b/wai/extensions/quantum-providers.md @@ -44,8 +44,12 @@ person's credential and signing key (the command line, the desktop app). 3. **Exact programs.** A client compiles a circuit exactly to the device's native gates and connectivity and checks the compiled program against the circuit before sending it. Angles cross the wire exactly (§4). -4. **Measured, not recomputed.** A provider's counts are sealed as measured on - the device it names (§8). Nothing recomputes them. +4. **Returned, not recomputed.** A provider's counts are sealed as the device + it names returned them (§8): `measured` for a processor, `simulated` for a + simulator or an emulator, which no device measured. The result says where + the counts come from, since counts rebuilt from probabilities are derived + and not shot records, and what was set off at the provider. Nothing + recomputes them. ## 2. Transports @@ -166,7 +170,7 @@ No fields. The answer describes the device: |---|---| | `protocol` | `"wai.quantum.provider/1"` | | `provider`, `device` | the provider's name for itself, and the device's | -| `provenance` | `silicon` (a processor), `emulator`, or `simulator`; with `simulator` (or `emulator`) a sentence naming what it is | +| `provenance` | `silicon` (a processor), `emulator`, or `simulator`; with `simulator` (or `emulator`) a sentence naming what it is, in the member of that name (`"simulator": "…"`) | | `qubits` | the device's qubit count | | `native_gates` | the gates it runs; a client compiles to the first of the bases of §4 whose every gate is listed | | `connectivity` | `"all"`, or the pairs `[a, b]` a two-qubit gate may act on, in either order | @@ -175,6 +179,7 @@ No fields. The answer describes the device: | `billing` | `{"free": true}`, or `{"unit", "price", "currency"}`: the billing unit (`shot`, `job`, `second`, …), the price of one as a decimal string (`"0.0003"`), and the currency or credit it is in; optionally `free_tier`, the free use offered and its limits, as the provider states them | | `max_shots` | optional: the most shots a job may ask | | `seeded` | optional: `true` when a job's `seed` makes its counts reproducible (a simulator) | +| `approximate` | optional, with `simulator` or `emulator`: `true` when its results are approximate (a truncated tensor network, a sampled Pauli path); a result is then labelled so (§8.1) | A client MUST NOT take a device to be free unless `billing` says `"free": true`, or an estimate says so (§5.2). @@ -221,9 +226,17 @@ Field: `job`. The answer, once the job is `done`: | `calibration` | optional: the calibration snapshot the job ran under, when it is not the one `describe` names | | `charged` | optional: `{"amount", "currency"}`, what the job cost | | `energy` | optional: `{"kwh"}`, the provider's own figure for the job's energy | +| `counts_source` | optional: where the counts come from (§8.3): `shots` (tallied from one record a shot), `vendor_counts` (the device's or service's own counts), or `probabilities` (rebuilt from probabilities); an answer without it is read as `vendor_counts` | +| `execution` | optional: how the device ran the program (§8.3): each of `mitigation`, `twirling`, `decoupling`, `remapping` and `optimisation` as `off`, `on`, `none` (the device has no such step) or `unknown` (a step the provider cannot vouch for), `verbatim` (`true` when the gates ran as written, on the qubits named), and an optional `note` | A `result` asked twice of the same job MUST give the same counts. +A provider that relays a job to another service (a wrapper around a service's +own interface) MUST give `execution`, from what it sets in its own request, and +MUST give `counts_source`, `probabilities` when it rebuilds counts from them. +Each is the provider's word, and sealed as such (§8.3). A client refuses a +`counts_source` or an `execution` not of the form above, and seals nothing. + ### 5.6 `cancel` Field: `job`. The answer's `status` is `cancelled`, or `done` for a job that @@ -279,40 +292,399 @@ that it can spend the person's money. ## 8. The receipt -The host seals each provider job as the job service seals its own: an Ed25519 -signature, by the host's key, over the request as submitted and the result -as returned, each in its RFC 8785 canonical text, verified by the same -verifier. The result's `method` is `provider` and its accuracy class is -`measured`. It carries: +The host seals each provider job as the job service seals its own, and the +same verifier checks both: an Ed25519 signature, by the host's key, over the +request as submitted and the result as returned (§8.2). A job's body is an +object holding `request`, the job as it was asked, and `result`, which carries +its `receipt`. This section says what a provider's result carries (§8.1), how +it is sealed and read (§8.2), what its counts' source and its `execution` say +(§8.3), what a verifier checks and the reason it gives for each failure +(§8.4), and what the receipt proves (§8.5). A verifier of §8.2 and §8.4 checks +any job's receipt, a provider's or not. -- `provider`: the person's name for it, its kind, its protocol, the device, - its provenance, and the BLAKE3 hash of its `describe` answer; -- `calibration`: the calibration snapshot the job ran under; -- `compiled`: the basis, the BLAKE3 hash of the compiled program's OpenQASM 3 - text, its gate and swap counts, its initial and final layout, the check it - passed before it was sent, and how it was placed (`placement`: with a - calibration read as a target, the target's hash, the layout's expected - success and the fewest-swaps layout's); -- `job`: the provider's job id and the status checks before it finished; -- `counts`, keyed by the program's classical bits (§6); -- `ideal`: the circuit's exact distribution set beside the counts (up to 22 - qubits): the total variation distance, the Hellinger fidelity, and the - distance the shots alone would leave on average; -- `cost`: the estimate shown before submitting, what the provider says it - charged, and whether the person confirmed a run that was not free; -- `energy`: unmetered here, with the provider's own figure, labelled as its - own, when it gives one. +### 8.1 What a provider's result carries -What the receipt proves: that this result, with these counts, is what the -signer's host received for this request from the provider and device it -names, under the calibration snapshot named, for the compiled program whose -hash it carries; and that the host compiled that program exactly and checked -it against the circuit before sending it. What it does not prove: that the -device ran that program, or ran anything. The counts are the provider's word, -and they are not reproducible by recomputation: the same job run again gives -other counts. The `ideal` distance says how far they are from what the -circuit gives; a distance well above the sampling distance is the device's -error, or a program the device did not run as sent. +| member | | +|---|---| +| `method` | `provider` | +| `accuracy` | `class`: `measured` when the provider's `provenance` is `silicon`, `simulated` when it is a simulator's or an emulator's (no device measured those counts); `approximate`: `true`, only on a `simulated` result whose provider's `describe` says `approximate` (§5.1), and absent otherwise; `note`, a sentence | +| `provider` | `name` (the person's name for it, the request's `provider`), `kind` (`sampler-v2`, `prx-circuits`, `native-turns`, `cloud-tasks`, `http` or `process`), `protocol` (`wai.quantum.provider/1`, or a built-in client's own), `device`, `provenance` (`silicon`; or `simulator` or `emulator`, alone or followed by `:` and the sentence naming it), `qubits` (the device's), `basis` (§4), and `describe`, the BLAKE3 of the RFC 8785 text (§8.2) of the provider's `describe` answer (for a built-in kind, of the description its client makes of the device). `provenance` is `silicon` exactly, or its part before any `:` is `simulator` or `emulator` | +| `calibration` | the calibration snapshot the job ran under, as the provider names it, or `null` | +| `compiled` | the program sent: `basis`; `qasm3`, its OpenQASM 3 text, `$i` being index `i` of the coupling map the provider described; `qubit_names`, the device's own name for each index the program uses, where a client sends it under those names (a `cloud-tasks` processor's labels, each index itself on its simulator; a `prx-circuits` device's qubits), each as it was when the job was sent; `program`, the BLAKE3 of `qasm3`'s UTF-8 bytes; `hash`, a sentence saying so; `native_gates`, `two_qubit_gates` and `swaps`, its counts; `layout` and `final_layout`, for each logical qubit the index it starts and ends on; `checked`, the check it passed before it was sent; `placement`, how it was placed (with a calibration read as a target, the target's hash, the layout's expected success and the fewest-swaps layout's) | +| `job` | `id`, the provider's id for the job, and `polls`, the status checks before it finished | +| `counts` | keyed by the program's classical bits (§6), each outcome that occurred with its count, at least 1 (a host drops an outcome a provider counts 0); with `distinct_outcomes`, the outcomes that occurred, and `omitted_shots`, the shots of outcomes not listed (at most 65,536 outcomes are listed). Every provider result an engine has sealed carries these, the earlier form's included | +| `shots` | the shots: the request's, 1024 when it gives none | +| `counts_source` | where the counts come from (§8.3) | +| `execution` | how the device was asked to run the program (§8.3) | +| `ideal` | the circuit's exact distribution p set beside the counts' frequencies q (up to 22 qubits): `computed`, and when it is `true` `total_variation_distance` (½ Σ \|q − p\|), `hellinger_fidelity` ((Σ √(p·q))²), `sampling_tvd_expected` (½ Σ √(2p(1 − p)/(π·shots)), the distance the shots alone leave on average, by the normal approximation) and `ideal_outcomes`; when `false`, `why`. The host computes them, and no verifier can without the program's simulation | +| `cost` | `free`; `estimate`, the estimate shown before submitting (an object, with `unknown`, a sentence, when none was known); `charged`, what the provider says it charged (`{"amount", "currency"}`), or `null`; `confirmed`, `true` exactly when the run was not free: such a run ran only with the person's confirmation (§7), and a free run needs none and records none | +| `energy` | `unmetered`, saying why it is not metered here; and, when the provider gives one, its own figure `service_kwh`, labelled as its own by `service_kwh_is` | +| `engine`, `schema` | the engine that made the result (`{"name": "wai-quantum", "version"}`) and `wai.quantum.job-result/1` | + +A result sealed before results carried `counts_source` and `execution` is of +the *earlier form*: it carries neither, its class is `measured` whatever ran +it, and some of the members above are missing. It verifies by §8.4's rules +for that form. A verifier reads its class by its provenance, and says so: a +simulator's counts sealed as `measured` are a simulator's. + +A job's `id` is the provider's, sealed: some name the person's account (a +cloud account's number inside a task's identifier). Sharing a receipt shares +it. + +### 8.2 How it is sealed, and how a job is read + +**The seal.** The result's `receipt` is an object: + +| member | | +|---|---| +| `alg` | `ed25519` | +| `canon` | `rfc8785`; absent on a receipt of the first form (below) | +| `key` | the signer's Ed25519 public key `A`, its 32 bytes as 64 hex digits | +| `digest` | the digest signed, 32 bytes as 64 hex digits | +| `signature` | the signature `R ‖ S`, 64 bytes as 128 hex digits | + +Hex digits are lower case, `0`–`9` and `a`–`f`, and nothing else. The receipt +is not sealed, so each member has one spelling. Any other member of the +receipt is ignored. + +The digest is BLAKE3 (32 bytes, the hash mode) of + + d ‖ u64(|Q|) ‖ Q ‖ u64(|T|) ‖ T + +where `d` is the 27 ASCII bytes `wai:quantum-service-receipt` and then the one +byte `0x02` (`0x01` for the first form); `Q` is the canonical text of `request`; +`T` is the canonical text of `result` with its `receipt` member removed; and +`u64(n)` is `n` as 8 bytes, big-endian. The signature is Ed25519 as RFC 8032 +§5.1 defines it, with no context and not prehashed, over the 32 bytes of the +digest. Since the domain byte is inside the digest, a receipt of one form does +not verify as the other. + +Only `request` and `result` are sealed. Whatever else a job's body holds (its +`status`, `id`, `method`, `why`, `estimate` and times) is the host's, and +unsealed: a verifier takes nothing from it, and its verdict does not change +with it. + +**Reading.** A job is read from its text, strictly, before anything is +checked. The text is UTF-8 with no byte order mark, and one JSON value +(RFC 8259) with whitespace allowed around it. It is unreadable when: + +- an object names a member twice (names compared after their escapes are read); +- a string holds a lone surrogate: a `\u` escape of a high surrogate not + followed at once by one of a low surrogate, or of a low surrogate alone; +- a string holds a control character (U+0000 to U+001F) not escaped; +- a number's magnitude is 2^1024 − 2^970 or more, which rounds to an infinite + double; +- arrays and objects nest deeper than 127, the outermost counted as the first. + +A noncharacter (U+FDD0 to U+FDEF, and the last two code points of each plane) +is a character like any other. Each number is read as the nearest IEEE 754 +double, ties to even; a number too small for any double other than zero reads +as zero. A whole number written as one (a literal with no fraction and no +exponent) in the request or the result that no double holds exactly says more +than the receipt seals, since the seal holds its double (§8.4, +`inexact_number`), unless it is the canonical text (below) of the double it +reads as. That one spelling is RFC 8785's for a whole double whose magnitude is +2^53 or more and under 10^21 (a negative one with its `-`), its shortest digits +padded with zeros (2^60 as +1152921504606847000), and what any JSON writer that follows RFC 8785 or +ECMAScript writes: it reads as the double that was sealed, and no other +spelling of an inexact whole number is taken. A canonical receipt seals every number as its double; a +first-form receipt seals a whole number written as one in its writer's integer +range by its digits, and every other number by its double (below), and only +what a receipt seals as a double is held to this. + +**The canonical text** of a value is RFC 8785's: + +- `true`, `false` and `null` as they are; +- a string between `"`s, with `"` and `\` escaped as `\"` and `\\`; U+0008, + U+0009, U+000A, U+000C and U+000D as `\b`, `\t`, `\n`, `\f` and `\r`; every + other character below U+0020 as `\u00` and two lower-case hex digits; and + every other character as its own UTF-8 bytes, unescaped (`/` and U+007F + included) and not normalised; +- a number as ECMAScript's Number::toString writes the double it reads as + (ECMA-262, 6.1.6.1.20): zero (also negative zero) as `0`; otherwise, with + `s` the fewest decimal digits `d1 … dk` that read back as the double (of + those, the nearest to it, a tie going to the even last digit) and `n` such + that the value is 0.`s` × 10^n: if k ≤ n ≤ 21, the digits and n − k zeros; + if 0 < n ≤ 21, the digits with a `.` after the first n; if −6 < n ≤ 0, `0.`, + −n zeros, and the digits; otherwise `d1`, then `.` and `d2 … dk` when k > 1, + then `e`, a sign (`+` or `-`), and |n − 1| in decimal; a negative number is + the same with `-` before it; +- an array as `[`, its values' texts joined by `,`, and `]`; +- an object as `{`, its members joined by `,`, each the name's text, `:` and + the value's text, in the order of their names compared as sequences of + UTF-16 code units (unsigned), and `}`; +- with no whitespace anywhere outside strings. + +So a result verifies after any JSON reader and writer has carried it, even +one that writes `1.0` as `1` or orders members otherwise. + +**The first form.** The first receipts, which name no `canon`, sealed the text +their signer's JSON writer wrote, not the canonical text, under the domain byte +`0x01`. A verifier reads `Q` and `T` as that writer did: the value's compact +text (no whitespace outside strings), its object members in the order the job +gives them (the result's `receipt` removed, the others kept in order), its +strings as in the canonical text, and its numbers so: + +- a whole number written as one (no fraction, no exponent), without a `-` and + from 0 to 2^64 − 1, or with a `-` and from −2^63 to −1, is written as its + decimal digits, with its `-` (so `-0` is a double: `-0.0`); +- every other number is written from its double: `-0.0` for negative zero, and + `0.0` for zero; otherwise, with `s` = `d1 … dk` and `n` as above, if n ≥ k + and n ≤ 16, the digits, n − k zeros and `.0`; if 0 < n ≤ 16, the digits with + a `.` after the first n; if −5 < n ≤ 0, `0.`, −n zeros and the digits; + otherwise `d1`, then `.` and `d2 … dk` when k > 1, then `e`, a sign (`+` + or `-`), and |n − 1| in decimal (`1e+16`, `1e-7`); a negative number is the + same with `-` before it. + +The first form was sealed from 2026-10-17 to 2026-10-20, every receipt by one +writer, whose text these rules restate. + +A first-form receipt verifies only as its signer wrote it: reordered members +break it, and so can a number written another way. No host writes the first +form now. A `canon` other than `rfc8785` (`null` among them) names no form. + +**The signature, strictly.** RFC 8032 leaves some choices open, and two +verifiers that chose differently would disagree on the same receipt. A +verifier here: + +- decodes `A` and `R` as RFC 8032 §5.1.3 does, and refuses an encoding whose y + (its low 255 bits, little-endian) is p = 2^255 − 19 or more, one that is not + on the curve, and one with x = 0 and the sign bit set; +- refuses `A` of small order (a point P with [8]P the identity) and `A` not in + the prime-order subgroup ([ℓ]A not the identity, ℓ = 2^252 + + 27742317777372353535851937790883648493); +- refuses `R` of small order, and `S` (little-endian) of ℓ or more; +- accepts exactly when [S]B = R + [k]A, with k = SHA-512(R ‖ A ‖ the 32 + digest bytes) read little-endian, mod ℓ, compared as points with no cofactor + (and so as their canonical encodings). + +Without the rules on `A`, the identity as `A`, any `S` and `R` = [S]B hold +for every digest: a receipt for any content at all. A library's default check +also takes the identity as `R` with `S` = 0. + +### 8.3 Where the counts come from, and how the device was asked to run them + +**`counts_source`** is one of: + +| value | the counts are | +|---|---| +| `shots` | tallied from the device's records, one a shot | +| `vendor_counts` | the provider's own counts, as it returned them | +| `probabilities` | rebuilt from the probabilities the device gave: each `p·shots` rounded down, then the shots left given one each to the largest remainders, a tie to the lower outcome. They are derived, and not shot records | + +Who did so is read from `provider.kind`. On a built-in kind the host's client +did, and it is the host's word. On `http` and `process` the host received counts +and did neither, and it is the provider's word for how it made them (§5.5): the +rule for rebuilding them is the one the provider says it kept, which the host +cannot vouch for. + +**`execution`** says what the host knows of the five steps a provider may +take between receiving a program and running it: `mitigation` (correcting +counts after the run, readout correction or debiasing), `twirling` (of gates +or of measurements), `decoupling` (dynamical decoupling of idle qubits), +`remapping` (moving the program to other qubits) and `optimisation` +(rewriting its gates). It is an object of: + +| member | | +|---|---| +| `stated_by` | `client`, the host's client from the request it sends; `provider`, the provider in its `result` answer (§5.5); or `nobody` | +| the five steps | each `off` or `on` (by a setting the client sent, or as the provider states), `default` (said by a client: its request sets nothing for it, so the provider's default stands, whatever it is), `none` (said by a provider: its device has no such step), or `unknown` (when nobody states it, or a provider cannot vouch for the step) | +| `verbatim` | `true` when the request asks for the gates to run as written, on the qubits named (a verbatim box); `false` when it does not; `null` when nobody states it | +| `sent` | with `client` only: `fields`, the members of the request's body, each once, in increasing order of their UTF-8 bytes; and `settings`, an object of what the request sets: JSON Pointers (RFC 6901) into the body, each with the value it holds there, and names for settings the body holds otherwise (`verbatim_box`); and nothing else | +| `note` | optional: a sentence | + +and nothing else. Its rules: with `stated_by` `client`, each step is `off`, +`on` or `default`, `verbatim` is `true` or `false`, and `sent` is given; with +`provider`, each step is `off`, `on`, `none` or `unknown`, `verbatim` is +`true` or `false`, and there is no `sent`; with `nobody`, each step is +`unknown`, `verbatim` is `null` and there is no `sent`. Each key of +`sent.settings` either begins with `/`, and its first reference token (up to +the next `/`, its `~1` and `~0` read as `/` and `~`) is one of `fields`, the +rest of it not read; or is a name, not empty, that does not begin with `/`. A program run as written is +neither remapped nor optimised, so with `verbatim` `true`, `remapping` and +`optimisation` are each `off` or `none`. A client that says a step is `off` or +`on` says by which setting: `settings` is then not empty. + +**What each built-in kind records.** A client builds its `execution` from the +very body it sends, so that what is stated is what was sent, and its counts' +source from what the service answered. These are their rows. A later client +that sends something else adds a row; a row is never removed, so every +receipt keeps verifying. A kind of the open protocol (`http`, `process`) +records the provider's word (`provider`), or `nobody` when it says nothing. On +any kind, a job recorded before its client stated what it sent, and collected +since, is stated by `nobody`: nothing else is known of what was sent. + +| kind | `counts_source` | `sent.fields` | `sent.settings` | steps | `verbatim` | +|---|---|---|---|---|---| +| `sampler-v2` | `shots` | `backend`, `params`, `program_id` | `/params/options/dynamical_decoupling/enable`: `false`, `/params/options/twirling/enable_gates`: `false`, `/params/options/twirling/enable_measure`: `false` | `decoupling` and `twirling` `off`; the other three `default` | `false` | +| `prx-circuits` | `vendor_counts` | `circuits`, `shots` | none: the client sends nothing else | all five `default` | `false` | +| `native-turns` | `probabilities`: the service answers probabilities, so its counts are always rebuilt | `backend`, `input`, `settings`, `shots`, `type`; and `noise`, on its simulator under a device's noise model | `/input/gateset`: `native`, `/settings/error_mitigation/debiasing`: `false`; and `/noise/model`: the model's name, with `noise` | `mitigation` `off`; the other four `default` | `false` | +| `cloud-tasks`, on a processor (`silicon`) | `shots`, from the task's rows; `probabilities`, when its result holds none | `action`, `clientToken`, `deviceArn`, `outputS3Bucket`, `outputS3KeyPrefix`, `shots` | `verbatim_box`: `true`, the program's gates in the service's verbatim box on physical qubits | `remapping` and `optimisation` `off`; the other three `default` | `true` | +| `cloud-tasks`, on a simulator | as on a processor | as on a processor | none | all five `default` | `false` | + +A row is the `execution` its kind states, less its `note`: `stated_by` is +`client`, and every member is as the row gives it. A row holds for its +provenance: `cloud-tasks`' first row for `silicon`, its second for any other +(a simulator or an emulator); `native-turns`' row with `noise` for any +provenance but `silicon`, its model's name a string, not empty, and its row +without `noise` for any. `prx-circuits` sends only +`circuits` and `shots`, so it turns nothing off, and says so. No row says a +provider runs a program as sent where its client sets nothing that asks it to. + +### 8.4 What a verifier checks + +A verifier reads the job (§8.2) and reports every reason below that applies, +as a set: none for a valid receipt. It is valid exactly when there is none. + +| reason | when | +|---|---| +| `unreadable` | the text breaks a rule of reading (§8.2). Reported alone | +| `not_a_job` | the value is not an object whose `request` and `result` are both objects. Reported alone | +| `no_receipt` | the result has no `receipt`. Reported alone | +| `receipt_form` | the receipt is not an object; its `alg` is not `ed25519`; it has a `canon` other than `rfc8785`; or its `key`, `digest` or `signature` is not lower-case hex of its length. When it is reported, the next three are not checked | +| `digest_mismatch` | the digest the receipt states is not the digest (§8.2) of this request and result | +| `bad_key` | `key` is refused by the strict rules (§8.2). When it is reported, the signature is not checked | +| `bad_signature` | the signature does not hold, strictly (§8.2), over the digest the receipt states (so a body changed after it was sealed gives `digest_mismatch` alone) | +| `inexact_number` | a whole number written as one, in what the receipt seals (the request, and the result less its receipt; for a set, the request and its head, §8.6) and sealed as a double (§8.2), is not a double, and is not the canonical text of the double it reads as. A receipt is of the first form when `receipt_form` is not reported and it has no `canon`; one that `receipt_form` refuses names no form, and its numbers are read as a canonical receipt's | +| `provider_class` | a provider's result whose `accuracy.class` is not the one its form gives: `measured` in the earlier form; in the stated form, `measured` for `silicon` and `simulated` for a provenance whose part before any `:` is `simulator` or `emulator` (none for any other); or whose `accuracy.approximate` is present and not `true`, or `true` in the earlier form or on a class other than `simulated`. Or a result of another method whose class is `measured` or `simulated` | +| `request_provider` | a provider's result whose `provider.name` is not a string equal to the request's `provider` | +| `request_shots` | a provider's result whose `shots` is not the request's (1024 when the request gives none, or `null`) | +| `counts` | a provider's result whose `counts` is not an object of outcomes, all of one length and each of `0` and `1` only, to whole numbers from 1 to 2^53 − 1; or whose `shots` (a whole number of at least 1), `omitted_shots` and `distinct_outcomes` (whole numbers of at least 0) are missing; or whose counts and `omitted_shots` do not sum to `shots`; or whose `distinct_outcomes` is not the number of outcomes listed when `omitted_shots` is 0; or, when it is not 0, lists other than 65,536 outcomes, or no more distinct outcomes than it lists | +| `provider_members` | a stated provider's result (below) without, or with a wrong type of: `provider`'s `name`, `kind`, `protocol` and `device` (each a string not empty), `provenance` (as `provider_class` reads it), `describe` (64 lower-case hex digits), `qubits` (a whole number of at least 1) and `basis` (equal to `compiled.basis`); `calibration` (a string or `null`); `compiled`'s `basis` (`rzsxxcz`, `prxcz` or `gpizz`), `program` (64 lower-case hex digits), `qasm3` (a string), `native_gates`, `two_qubit_gates` and `swaps` (whole numbers of at least 0), `layout` and `final_layout` (arrays of whole numbers of at least 0) and `checked` (a string); `compiled.qubit_names`, where it is given or required, is below; `job`'s `id` (a string not empty) and `polls` (a whole number of at least 0); `ideal.computed` (`true` or `false`); `cost`'s `free` and `confirmed` (each `true` or `false`), `estimate` (an object) and `charged` (`null`, or an object whose `amount` and `currency` are strings); `energy.unmetered` (a string), and `energy.service_kwh_is` (a string) when there is `energy.service_kwh`; `schema` (`wai.quantum.job-result/1`); `engine.name` (`wai-quantum`) and `engine.version` (a string) | +| `counts_source` | a stated provider's result whose `counts_source` is not one of §8.3's values, or, for a kind with rows, not one the rows that hold for its provenance give | +| `execution` | a stated provider's result whose `execution` breaks a rule of §8.3; or, for a kind with rows, is neither one of the rows that hold for its provenance nor stated by `nobody`; or, for `http` and `process`, is not stated by `provider` or `nobody` | +| `compiled_program` | a stated provider's result whose `compiled.program` is not the BLAKE3 of `compiled.qasm3`'s UTF-8 bytes, when `qasm3` is a string and `program` is 64 lower-case hex digits | +| `cost_confirmation` | a stated provider's result whose `cost.confirmed` is `true` on a free run or `false` on one that is not free (§7), when both are `true` or `false` | + +A provider's result is one whose `method` is `provider`. It is *stated* when it +carries a member `counts_source` or a member `execution` (whatever their +values), and of the earlier form when it carries neither; the earlier form is +held to `provider_class`, `request_provider`, `request_shots` and `counts` +only. A whole number is any number whose value is whole, however it is +written (`1`, `1.0`, `1e0`), however large; only a count's value has a ceiling, +and a sum is exact, not a double's. A number's value, in every rule here and on +every form of receipt, is its nearest double (§8.2): the digits of a first-form +receipt are for what it sealed, and for nothing checked here. `-0` is 0. +"The request's" is by value: two equal strings are the same, and that a string +is no number is another rule's to say. +A class a verifier does not know is not a reason: a later version may add one +(VERSIONING.md). + +`compiled.qubit_names` is required of a stated result of `cloud-tasks` or +`prx-circuits` whose `execution` is stated by `client`, and optional otherwise. +When given (`null` is given, and is no object) it is an object: each key an index +in decimal, with no leading zero; each value a string, not empty; the value of +every entry of `layout` and `final_layout`, written in decimal in full, is among +its keys; and so is every index `qasm3` acts on, when `qasm3` is a string: each +`$` anywhere in it followed by one or more of the ASCII digits 0 to 9 names the +index those digits read as, written in decimal with no leading zero (a `$` +followed by no such digit names none). Otherwise it is a `provider_members` reason. + +**What a host seals, its own verifier accepts.** A host refuses a request holding +a whole number past ±(2^53 − 1), or any number that it or RFC 8785 would write in +a spelling this section refuses, where it reads it, on every path to a seal (a +set's own request, its sweep's values among them, included); and it writes a +whole number no double holds in a result as the double it seals. A receipt an +earlier release sealed with such a number is refused with `inexact_number`, +though its signature holds: what it sealed is the double, and its text says +more. Written as that double's exact digits, or as its canonical text, it +verifies. The local hosts' `qec` took a seed of any size; their compile, pulse, +estimate and `@Seal` took any number in a request; and a set took any number in +its own request and its sweep's values. The job service refused such a number +in every request but a set's own. + +A verifier also states, for a provider's result (one with no receipt +included), its class as its provenance reads it, and, in the stated form, its +counts' source and its `execution`, each as the result gives it. + +These are all the rules: what §8.1 names and this table does not check +(`compiled.hash` and `placement`, the notes, the `ideal` figures, the +estimate's contents) is the host's word, unchecked. The schemas +(`wai.quantum.job-result/1`, `wai.quantum.receipt/2`) describe what a host +writes; a verifier judges by this section. + +### 8.5 What the receipt proves, and what it does not + +A valid receipt proves that the holder of the key it names signed this +request and this result, nothing else and nothing less: every member of +both, the counts, `counts_source` and `execution` among them. It does not +say whose key it is. The job service publishes its keys and their windows +(`GET /.well-known/wai-quantum-receipt-keys`); a host's key is the person's +own, and anyone relying on a receipt decides whether they trust it. Nothing +sealed carries a time: a receipt can be shown again, and no window can be +applied to it from its bytes. For the same reason a host can still seal a +result of the earlier form, stating neither its counts' source nor its +`execution`; a verifier then holds it to the earlier form's rules, reads its +class by its provenance, and says that it states neither. A host can likewise +seal a stated result with `execution` stated by `nobody` on any kind, meant for +a job recorded before its client said what it sent: that says nothing of what +was sent, and a verifier says that nobody stated it. + +Everything the result says the host received, sent and checked is the host's +own word, signed: that this was the provider's answer, under the calibration +snapshot named, and that the host compiled the program whose text and hash it +carries exactly and checked it against the circuit before sending it. The +compiled program's text is in the receipt, with the device's names for its +qubits where its client uses them, so anyone can read which gates were sent to +which qubits and check its hash. It is not the bytes sent: a client writes the +same gates in what its service takes. `execution` says what the request +asked of the provider. What it does not prove: that the device ran that +program, or ran anything. The counts are the provider's word, and they are +not reproducible by recomputation: the same job run again gives other counts. +A provider's simulator's counts say what its software gives, which is why +they are `simulated`, not `measured`. The `ideal` distance says how far the +counts are from what the circuit gives; a distance well above the sampling +distance is the device's or the simulator's error, or a program not run as +sent. + +### 8.6 A set's receipt + +A set is one request that runs many jobs (`programs`, a list of programs or +jobs, or `sweep`, values for the inputs of the request's own `qasm`); its result +has `"format": "wai.quantum.set/1"`. Its `head` is its result less `items` and +`receipt`. The set's receipt seals the set's request and its head, by §8.2, as a +job's seals its request and result; each item that ran carries a result with a +receipt of its own, over its own request. A verifier reads a set as it reads a +job (§8.2), and reports, beside §8.4's reasons for the set's request and head +(`unreadable`, `not_a_job`, `no_receipt`, `receipt_form`, `digest_mismatch`, +`bad_key`, `bad_signature`, `inexact_number`; the provider rules do not apply to +a head), each of these that applies: + +| reason | when | +|---|---| +| `set_items` | `items` is not a non-empty array whose `i`-th entry is an object with `index` equal to `i` (a whole number), `request` an object, exactly one of the members `result` (an object) and `error` (a string) present (a member that is present is so whatever its value, `null` included), and `path` an array of strings each of 64 lower-case hex digits. When it is reported so, the next two are not checked. Or: `count` is not the number of items; or the items are not the ones the request gives. With `programs` (an array, and no `sweep`), item `i`'s request is the request less `programs`, `sweep` and `dry_run`, with program `i` over it: a string as `qasm`, or an object's members, each replacing one of the same name. With `sweep` (an object, and no `programs`), each of its values is a non-empty array, the items are as many as the product of their lengths, and each item's request less `qasm` is the request less `programs`, `sweep`, `dry_run` and `qasm`, its `qasm` a string (the request's program written out at its point, which §8 does not check: below). A request with neither, or with both, gives no items; a member that is `null` is none. Requests are compared as JSON values: members in any order, numbers by value | +| `set_item` | an item with a `result` is not a valid job: `{"request": its request, "result": its result}`, read as §8.2 reads a job, gives any reason of §8.4; or its result is a set's (`format` `wai.quantum.set/1`: an item is one job); or its receipt is valid and names a `key` other than the set's receipt's `key` member as it stands (one the set's receipt does not have included). Or an item with an `error` has a request that writes a whole number its leaf could not seal: as `inexact_number` reads one in a canonical receipt, whatever the set's form, since a leaf is the canonical form's digest | +| `set_root` | `root` is not 64 lower-case hex digits, or not the Merkle root of the items' leaves; or an item's `path` does not lead from its leaf to `root` | + +An item's leaf is the digest (§8.2, the canonical form's) of its request and its +result less its `receipt`, or, for an item with an `error`, of its request and +`{"error": the error}`. The Merkle tree: a leaf's hash is BLAKE3 of +`moq-jwp:merkle-leaf` and the byte `0x01`, then the leaf; a node's is BLAKE3 of +`moq-jwp:merkle-node` and the byte `0x01`, then the left hash and the right; a +level of odd length pairs its last hash with itself; the root of one leaf is its +hash. A path lists the sibling at each level from the leaf up: with `n` items it +has as many entries as halvings (rounding up) take `n` to 1, and a path of any +other length does not lead to the root; at each level the item's position, +halved at each step, says whether the sibling is on the right (an even position) +or the left. This is the tree the WAI worlds' and provenance receipts use, its +domains theirs, kept so that every set already sealed verifies. Two things +follow from it. The root alone does not fix the number of items (a last leaf +paired with itself gives the root a level of one more would), and `count`, +sealed in the head, does; one item shown with its path and the head alone is +placed by `count`. And the root does not say whether an item failed: an item +with an `error` and one whose result is `{"error": …}` have one leaf, and only +the item's own receipt tells them apart. + +An item is read for its `index`, `request`, `result`, `error` and `path`; what +else it holds (its `status`, `method`, `why`, a sweep's `point`) is the host's, +unsealed, as a job body's members beyond its request and result are, and a +member its own receipt holds beyond its five is ignored, as a job's is. + +The engine also writes each item of a sweep out again at its point, and says +whether each `qasm` is that program (`derived`, beside the reasons); that is the +engine's check of the set, not a rule of its receipt, and a verifier that does +not write programs out does not make it. ## 9. Conformance @@ -339,7 +711,31 @@ The reference provider (`wai-quantum providers reference --stdio`, or `--http ADDR`) is this protocol around WAI's exact simulator. It is the provider the tests run against over both transports, and the template to copy: a provider for any device is the reference with its simulation replaced -by the device. +by the device. It states its counts' source and its `execution` (§5.5). + +**Receipts.** `quantum-conformance/receipts/` holds job receipts and what a +verifier must say of each (§8.4): receipts made by the command line on this +machine and by the job service, provider jobs of every built-in kind through +services that follow their contracts, the earlier form, the first (receipts the +first form's own writer sealed, and others `generate.py` made from §8.2's text), +and receipts sealed to break one rule each. A +receipt from a person's account names that account, and is not published: it is +checked with the differential's `--extra`, outside CI. A receipt once made is +kept, pinned by its digest. Two verifiers judge every one of them, and every +receipt made from a valid one by changing one member, numbers among them to +values past a double's range of whole numbers: `wai-quantum verify`, and +`quantum-conformance/tools/receipt_verify.py`, written from §8 alone in another +language and sharing no code with the engine. They must report the same reasons +for every receipt, and the manifest's for each in the corpus. Every other +surface that verifies is held to the manifest by its own tests, given each +receipt's text: the job service over HTTP and as a tool, the command line's tool +(each takes the job's text), the Python package (text, bytes or a path), the +desktop app and a browser tab. A surface given a job already parsed into objects +(a tool's `request` and `result`, a Python dict) cannot see what parsing loses: +a member named twice (`unreadable`), and a whole number past what its reader +keeps (`inexact_number`). It reports every other reason, and is held to the +manifest on every receipt but those. A tool's text is a JSON string, which holds +UTF-8 only, so text that is not UTF-8 reaches a verifier only as a file's bytes. ## 10. Versioning @@ -347,3 +743,10 @@ by the device. string; a provider that speaks only this one answers `unsupported_version` to another. Fields marked optional may be added to answers without a new version, and a client MUST ignore fields it does not know. + +A result's receipt seals every member it holds, so a member a later text adds +to the result is in no receipt sealed before it, and every such receipt +verifies as it did (§8.4's earlier form). `counts_source`, `execution`, +`compiled.qasm3`, the class `simulated` and `accuracy.approximate` were added +so, as were the answers' `counts_source` and `execution` and `describe`'s +`approximate`. diff --git a/wai/quantum-conformance/README.md b/wai/quantum-conformance/README.md index a05beafd9..a6f8f8533 100644 --- a/wai/quantum-conformance/README.md +++ b/wai/quantum-conformance/README.md @@ -25,6 +25,13 @@ 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. +Job receipts, as the provider protocol's §8 states them (the seal, its strict +reader and signature, and the rules of a provider's result), are `receipts/`, with +a README of its own: receipts the command line, the job service and every built-in +provider kind made, and receipts crafted to break one rule each, judged by +`wai-quantum verify` and by a second verifier written from §8 alone +(`tools/receipt_verify.py`), held to each other by `tools/receipt_differential.py`. + An entry with no `capability` is a `wai.quantum.circuit` vector. The trees are the one reference all three surfaces are checked against, rather than being checked against each other. diff --git a/wai/quantum-conformance/receipts/README.md b/wai/quantum-conformance/receipts/README.md new file mode 100644 index 000000000..605f35a8e --- /dev/null +++ b/wai/quantum-conformance/receipts/README.md @@ -0,0 +1,82 @@ +# Job receipts + +A job's receipt, as [the provider protocol's §8](../../extensions/quantum-providers.md#8-the-receipt) +states it, judged by two verifiers that share no code. Each must give every receipt here +exactly the reasons the manifest names (§8.4): none for a valid one. + +| file | what | +|---|---| +| `manifest.json` | every receipt in `jobs/`, the reasons a verifier must give it, how it was made, and what it is. Every expectation is written by hand from §8.4; none is copied from a verifier's output | +| `jobs/cli-*.json` | made by the command line on this machine, sealed with the published demo key (`make.sh`): runs by several methods, a compile, and runs on the reference provider over a local process, free and priced | +| `jobs/service-*.json` | made by the free job service, sealed with its key (`make.sh`) | +| `jobs/scripted-*.json` | provider jobs of every built-in kind (`sampler-v2`, `prx-circuits`, `native-turns` with and without a noise model, `cloud-tasks` on a processor and on its simulator, with rows of shots and with probabilities), each through a service that follows that provider's published contract, sealed with the demo key (`wai-quantum-qpu/tests/clients.rs`, `every_built_in_kind_states_what_it_sent_and_its_receipt_keeps_every_rule`, with `WAI_QUANTUM_RECEIPT_CORPUS` set to this directory) | +| `jobs/first-form-*.json` | sealed by the job service as it was on 18 October 2026 (commit 72d18742d), so by the first form's own writer, not by a restatement of it | +| `jobs/fixture-*.json` | the engine's pinned fixtures, sealed by earlier engines: a run before results named their engine, a provider job of the earlier form, and a compile | +| `jobs/crafted-*.json` | made by `generate.py` from the receipts above, each changed one way: the seal's form, the strict signature (a weak-key forgery, `S + ℓ`, a mixed-order key, …), the first form, what any JSON reader and writer may do (and so must change nothing), the strict reader, and each rule of a provider's result, each sealed again with the demo key where the change is inside the seal so that one rule alone breaks | + +The made receipts are kept as they were made, each pinned by its digest in `made.lock.json`: +a receipt missing or changed fails `generate.py --check`. A receipt made again differs in its +times (and, from the service, perhaps its key) and verifies all the same, so none is made again +to pass a test. A row of §8.3 that a client stops writing is held by the receipt that has it. + +## The two verifiers + +- `wai-quantum verify --json FILE` (and `--lines FILE`, one job a line): the reference, + `wai_quantum::engine::job::check`. +- [`../tools/receipt_verify.py`](../tools/receipt_verify.py): written in Python from §8 + alone, by a reader who had not seen the reference. Standard library only: its BLAKE3, + Ed25519, RFC 8785 text and strict reader are its own, each held to published answers by + `--self-test`. + +[`../tools/receipt_differential.py`](../tools/receipt_differential.py) holds them to each +other: + +```sh +python3 -I receipts/generate.py --check # the crafted half is as written +python3 -I tools/receipt_verify.py --self-test ../wai-quantum/src/engine/testdata/es-number-tostring.txt +python3 -I tools/receipt_differential.py --reference PATH/wai-quantum +python3 -I tools/receipt_differential.py --reference PATH/wai-quantum --plant lenient-key +python3 -I tools/receipt_differential.py --second-only # no build: the second verifier and the manifest +``` + +The differential requires, for every receipt here, that both verifiers and the manifest name +the same reasons, and that both verifiers state the same key, form, class read by provenance +and counts' source. Then it changes every valid receipt one member at a time (each member of +its request, its result, its receipt and its unsealed envelope, removed, and its value +replaced) and requires the same reasons from both for every change, and two oracles beside +them: a change inside the request, the result or the receipt is never valid, and a change to +the unsealed envelope, or a member added to the receipt, never changes the verdict. + +It also changes each number of the provider receipts to values past a double's whole numbers, +and sets a job's shots in both its request and its result, to see a limit one verifier has and +the other does not; and it seals those of the demo key again, so that each is judged by what it +says and not only by its digest. + +`--plant NAME` runs the second verifier with a defect planted in it: a lenient key check, +`alg` unchecked, the last of a member named twice kept, inexact numbers let through, a +simulator's counts let pass as measured, a ceiling on every whole number, `R` of small order +let through. The differential succeeds only when it catches the plant. That is the proof that +it can fail. + +`--extra DIR` adds receipts a person holds and does not publish: both verifiers must find each +valid. A receipt from a cloud account's task names that account's number in its job id, and +the receipt seals it, so the corpus holds none: the shape of such a receipt is here +(`scripted-cloud-tasks-*`), and real ones are checked with `--extra`. + +CI runs the whole differential beside the build of the command line +(`.github/workflows/wai-quantum-cli.yml`, job `cli-linux`), and the second verifier against +the manifest, with its plants, among the independent verifiers +(`.github/wai-conformance/verifiers/quantum-receipts.sh`). + +## Making it again + +```sh +bash receipts/make.sh PATH/wai-quantum receipts/jobs # the command line's and the service's +(cd ../wai-quantum-qpu && WAI_QUANTUM_RECEIPT_CORPUS=$PWD/../quantum-conformance/receipts/jobs \ + cargo test --test clients every_built_in_kind) # the built-in kinds' +python3 -I receipts/generate.py # the crafted half and the manifest +``` + +Making them again replaces receipts a release already judged, and `--check` then fails: add +receipts under new names, and keep the ones there. A new name is added to `MADE` in +`generate.py`; `generate.py --pin NAME` records its digest, and refuses a name it holds. diff --git a/wai/quantum-conformance/receipts/generate.py b/wai/quantum-conformance/receipts/generate.py new file mode 100644 index 000000000..3d231398a --- /dev/null +++ b/wai/quantum-conformance/receipts/generate.py @@ -0,0 +1,885 @@ +#!/usr/bin/env python3 +"""generate.py: the crafted half of the receipt corpus (README.md). + + python3 -I generate.py # writes jobs/crafted-*.json and manifest.json + python3 -I generate.py --check # fails unless both are what it would write + +The corpus has two halves. The made half (`jobs/cli-*`, `jobs/service-*`, +`jobs/scripted-*`, `jobs/fixture-*`) was sealed by the command line, the job +service, the provider clients and earlier engines, and is kept as it was made: +nothing here writes it. The crafted half is made here from the made half, each +receipt changed in one way and, where the change is inside what is sealed, +sealed again with the published demo key (seed 42 repeated, the key +`wai-quantum --demo-key` signs with), so that the one rule it breaks is the +only thing wrong with it. The manifest names, for every receipt in both halves, +the reasons a verifier must give (wai/extensions/quantum-providers.md +section 8.4), each written here by hand from that section: none is copied from +a verifier's output. + +To seal, it needs BLAKE3, the RFC 8785 text and Ed25519 signing. BLAKE3 is the +integer-only referee's (../tools/referee.py, which shares no code with the +engine); the other two are written below. Neither verifier is imported: the +two verifiers judge what this writes, and must agree with each other and with +the manifest. + +Standard library only, Python 3.9+. +""" +import copy +import hashlib +import importlib.util +import json +import os +import struct +import sys +from decimal import Decimal + +HERE = os.path.dirname(os.path.abspath(__file__)) +JOBS = os.path.join(HERE, "jobs") + +sys.dont_write_bytecode = True +_spec = importlib.util.spec_from_file_location("referee", os.path.join(HERE, "..", "tools", "referee.py")) +_referee = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(_referee) +blake3 = _referee.blake3 + +# ------------------------------------------------------------------ JSON text + +class Raw(object): + """A number written as this literal, whatever value it stands for.""" + + def __init__(self, literal, value): + self.literal, self.value = literal, value + + +def es_number(x): + """ECMAScript's Number::toString of the double x (section 8.2).""" + x = float(x) + if x == 0: + return "0" + sign = "-" if x < 0 else "" + t = Decimal(repr(abs(x))).as_tuple() + digits = "".join(map(str, t.digits)).rstrip("0") or "0" + k = len(digits) + n = len("".join(map(str, t.digits))) + t.exponent + if k <= n <= 21: + body = digits + "0" * (n - k) + elif 0 < n <= 21: + body = digits[:n] + "." + digits[n:] + elif -6 < n <= 0: + body = "0." + "0" * (-n) + digits + else: + e = n - 1 + body = digits[0] + ("." + digits[1:] if k > 1 else "") + "e" + ("+" if e >= 0 else "-") + str(abs(e)) + return sign + body + + +def first_number(v): + """A number as the first form's writer wrote it (section 8.2).""" + if isinstance(v, bool): + raise TypeError + if isinstance(v, int) and (0 <= v < 2 ** 64 or -2 ** 63 <= v < 0): + return str(v) + x = float(v) + if x == 0: + return "-0.0" if str(x).startswith("-") else "0.0" + sign = "-" if x < 0 else "" + t = Decimal(repr(abs(x))).as_tuple() + digits = "".join(map(str, t.digits)).rstrip("0") or "0" + k = len(digits) + n = len("".join(map(str, t.digits))) + t.exponent + if k <= n <= 16: + body = digits + "0" * (n - k) + ".0" + elif 0 < n <= 16: + body = digits[:n] + "." + digits[n:] + elif -5 < n <= 0: + body = "0." + "0" * (-n) + digits + else: + e = n - 1 + body = digits[0] + ("." + digits[1:] if k > 1 else "") + "e" + ("-" if e < 0 else "+") + str(abs(e)) + return sign + body + + +_SHORT = {'"': '\\"', "\\": "\\\\", "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f", "\r": "\\r"} + + +def string_text(s): + out = ['"'] + for c in s: + if c in _SHORT: + out.append(_SHORT[c]) + elif ord(c) < 0x20: + out.append("\\u%04x" % ord(c)) + else: + out.append(c) + out.append('"') + return "".join(out) + + +def utf16(s): + return s.encode("utf-16-be") + + +def text(v, form="canonical"): + """The canonical text of v, or its first-form text (members in order).""" + if v is None: + return "null" + if v is True: + return "true" + if v is False: + return "false" + if isinstance(v, Raw): + return text(v.value, form) + if isinstance(v, (int, float)): + return es_number(v) if form == "canonical" else first_number(v) + if isinstance(v, str): + return string_text(v) + if isinstance(v, list): + return "[" + ",".join(text(x, form) for x in v) + "]" + keys = sorted(v, key=utf16) if form == "canonical" else list(v) + return "{" + ",".join(string_text(k) + ":" + text(v[k], form) for k in keys) + "}" + + +def dump(v, indent=0): + """A readable file's text of v: two-space indents, Raw literals as given.""" + pad, inner = " " * indent, " " * (indent + 1) + if isinstance(v, Raw): + return v.literal + if isinstance(v, dict): + if not v: + return "{}" + return "{\n" + ",\n".join(inner + json.dumps(k, ensure_ascii=False) + ": " + dump(x, indent + 1) for k, x in v.items()) + "\n" + pad + "}" + if isinstance(v, list): + if not v: + return "[]" + return "[\n" + ",\n".join(inner + dump(x, indent + 1) for x in v) + "\n" + pad + "]" + if isinstance(v, float) and v == 0 and str(v).startswith("-"): + return "-0.0" + return json.dumps(v, ensure_ascii=False) + + +# ---------------------------------------------------------------- Ed25519 (RFC 8032) + +P = 2 ** 255 - 19 +L = 2 ** 252 + 27742317777372353535851937790883648493 +D = -121665 * pow(121666, P - 2, P) % P +SQRT_M1 = pow(2, (P - 1) // 4, P) + + +def _add(a, b): + x1, y1, z1, t1 = a + x2, y2, z2, t2 = b + A, B = (y1 - x1) * (y2 - x2) % P, (y1 + x1) * (y2 + x2) % P + C, E = t1 * 2 * D * t2 % P, z1 * 2 * z2 % P + e, f, g, h = B - A, E - C, E + C, B + A + return (e * f % P, g * h % P, f * g % P, e * h % P) + + +def _mul(s, pt): + q = (0, 1, 1, 0) + while s: + if s & 1: + q = _add(q, pt) + pt = _add(pt, pt) + s >>= 1 + return q + + +def _encode(pt): + x, y, z, _ = pt + zi = pow(z, P - 2, P) + x, y = x * zi % P, y * zi % P + return (y | ((x & 1) << 255)).to_bytes(32, "little") + + +def _decode(b): + y = int.from_bytes(b, "little") & ((1 << 255) - 1) + sign = b[31] >> 7 + if y >= P: + return None + xx = (y * y - 1) * pow(D * y * y + 1, P - 2, P) % P + x = pow(xx, (P + 3) // 8, P) + if (x * x - xx) % P: + x = x * SQRT_M1 % P + if (x * x - xx) % P: + return None + if x == 0 and sign: + return None + if x & 1 != sign: + x = P - x + return (x, y, 1, x * y % P) + + +BASE = _decode((4 * pow(5, P - 2, P) % P).to_bytes(32, "little")) + + +def public_key(seed): + h = hashlib.sha512(seed).digest() + a = int.from_bytes(h[:32], "little") + a = (a & ((1 << 254) - 8)) | (1 << 254) + return _encode(_mul(a, BASE)) + + +def sign(seed, msg): + h = hashlib.sha512(seed).digest() + a = int.from_bytes(h[:32], "little") + a = (a & ((1 << 254) - 8)) | (1 << 254) + A = _encode(_mul(a, BASE)) + r = int.from_bytes(hashlib.sha512(h[32:] + msg).digest(), "little") % L + R = _encode(_mul(r, BASE)) + k = int.from_bytes(hashlib.sha512(R + A + msg).digest(), "little") % L + return R + ((r + k * a) % L).to_bytes(32, "little") + + +DEMO = bytes([42] * 32) +OTHER = bytes([43] * 32) +IDENTITY = bytes([1] + [0] * 31) +# A point of order 8. +ORDER8 = bytes.fromhex("c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a") + +# --------------------------------------------------------------------- sealing + +DOMAIN = b"wai:quantum-service-receipt" + + +def digest(request, result, form="canonical"): + q = text(request, form).encode("utf-8") + t = text({k: v for k, v in result.items() if k != "receipt"}, form).encode("utf-8") + return blake3(DOMAIN + bytes([2 if form == "canonical" else 1]) + struct.pack(">Q", len(q)) + q + struct.pack(">Q", len(t)) + t) + + +def seal(body, seed=DEMO, form="canonical"): + """body with its result sealed again by seed's key, in form.""" + d = digest(body["request"], body["result"], form) + receipt = {"alg": "ed25519", "canon": "rfc8785", "key": public_key(seed).hex(), "digest": d.hex(), "signature": sign(seed, d).hex()} + if form != "canonical": + del receipt["canon"] + out = copy.deepcopy(body) + out["result"] = {k: v for k, v in out["result"].items() if k != "receipt"} + out["result"]["receipt"] = receipt + return out + + +MERKLE_LEAF = b"moq-jwp:merkle-leaf\x01" +MERKLE_NODE = b"moq-jwp:merkle-node\x01" + + +def merkle_levels(leaves): + """Each level of the tree over leaves (section 8.6), the leaves' hashes first.""" + level = [blake3(MERKLE_LEAF + x) for x in leaves] + levels = [level] + while len(level) > 1: + if len(level) % 2: + level = level + [level[-1]] + levels[-1] = level + level = [blake3(MERKLE_NODE + level[i] + level[i + 1]) for i in range(0, len(level), 2)] + levels.append(level) + return levels + + +def set_leaf(item): + """An item's leaf (section 8.6): the digest of its request and its result, or its error.""" + if "result" in item: + return digest(item["request"], item["result"]) + return digest(item["request"], {"error": item["error"]}) + + +def seal_set(body, seed=DEMO): + """A set with its root and paths made from its items, and its receipt over + its request and its head, sealed again.""" + out = copy.deepcopy(body) + r = out["result"] + levels = merkle_levels([set_leaf(it) for it in r["items"]]) + r["root"] = levels[-1][0].hex() + for i, it in enumerate(r["items"]): + path, at = [], i + for level in levels[:-1]: + path.append(level[at ^ 1].hex()) + at //= 2 + it["path"] = path + head = {k: v for k, v in r.items() if k not in ("items", "receipt")} + d = digest(out["request"], head) + r["receipt"] = {"alg": "ed25519", "canon": "rfc8785", "key": public_key(seed).hex(), "digest": d.hex(), "signature": sign(seed, d).hex()} + return out + + +def edited(body, edit, form="canonical"): + """body changed by edit (which may change request, result or both), sealed again.""" + b = copy.deepcopy(body) + edit(b) + return seal(b, form=form) + + +def rx(body, edit): + """body with only its receipt changed by edit: nothing sealed again.""" + b = copy.deepcopy(body) + edit(b["result"]["receipt"]) + return b + + +def load(name): + with open(os.path.join(JOBS, name), encoding="utf-8") as f: + return json.load(f) + + +# --------------------------------------------------------------------- the cases + +def edited_set(body, edit): + """A set's result changed by edit, nothing sealed again.""" + b = copy.deepcopy(body) + edit(b["result"]) + return b + + +def edited_set_head(body, edit): + """A set's head changed by edit and the set's receipt sealed again over it, + its root and paths left as they are.""" + b = copy.deepcopy(body) + edit(b["result"]) + head = {k: v for k, v in b["result"].items() if k not in ("items", "receipt")} + d = digest(b["request"], head) + b["result"]["receipt"] = {"alg": "ed25519", "canon": "rfc8785", "key": public_key(DEMO).hex(), "digest": d.hex(), "signature": sign(DEMO, d).hex()} + return b + + +def cases(): + """(file name, the text, the reasons, what it is), every crafted receipt.""" + run = load("cli-run-observables.json") + prov = load("cli-provider-reference.json") + legacy = load("fixture-provider-job-legacy.json") + sampler = load("scripted-sampler-v2.json") + prx = load("scripted-prx-circuits.json") + turns = load("scripted-native-turns.json") + sim = load("scripted-cloud-tasks-simulator.json") + qpu = load("scripted-cloud-tasks-processor.json") + progs = load("cli-set-programs.json") + sweep = load("cli-set-sweep.json") + out = [] + + def add(name, body_or_text, reasons, what): + t = body_or_text if isinstance(body_or_text, (str, bytes)) else dump(body_or_text) + "\n" + out.append(("crafted-" + name + ".json", t, sorted(reasons), what)) + + # --- the seal's form (section 8.2): one spelling of each member + add("seal-alg-other", rx(run, lambda r: r.update(alg="rsa-pss")), ["receipt_form"], "an alg other than ed25519") + add("seal-canon-null", rx(run, lambda r: r.update(canon=None)), ["receipt_form"], "canon present and null: no form") + add("seal-canon-other", rx(run, lambda r: r.update(canon="jcs")), ["receipt_form"], "canon naming no form") + add("seal-key-upper", rx(run, lambda r: r.update(key=r["key"].upper())), ["receipt_form"], "the key in upper-case hex: a second spelling of one key") + + def plus(r): + d = r["digest"] + i = next(i for i in range(0, 64, 2) if d[i] == "0") + r["digest"] = d[:i] + "+" + d[i + 1:] + if any(run["result"]["receipt"]["digest"][i] == "0" for i in range(0, 64, 2)): + add("seal-digest-plus", rx(run, plus), ["receipt_form"], "a + where a hex digit belongs, which a lenient hex reader takes") + add("seal-key-two-byte-char", rx(run, lambda r: r.update(key="aé" + r["key"][3:])), ["receipt_form"], "a two-byte character among the key's hex digits") + add("seal-signature-short", rx(run, lambda r: r.update(signature=r["signature"][:-2])), ["receipt_form"], "a signature one byte short") + add("seal-key-missing", rx(run, lambda r: r.pop("key")), ["receipt_form"], "no key") + b = copy.deepcopy(run) + b["result"]["receipt"] = "VALID" + add("seal-receipt-string", b, ["receipt_form"], "a receipt that is not an object") + + # --- the signature, strictly + forged = copy.deepcopy(run) + forged["result"]["expectations"][0]["value"] = -1.0 + d = digest(forged["request"], forged["result"]) + forged["result"]["receipt"] = {"alg": "ed25519", "canon": "rfc8785", "key": IDENTITY.hex(), "digest": d.hex(), "signature": (IDENTITY + bytes(32)).hex()} + add("seal-weak-key-forgery", forged, ["bad_key"], "the identity as the key and as R, S = 0: a library's lenient check takes it for any digest, so any content verifies") + any_digest = copy.deepcopy(forged) + B = _encode(BASE) + any_digest["result"]["receipt"]["signature"] = (B + (1).to_bytes(32, "little")).hex() + add("seal-weak-key-any-digest", any_digest, ["bad_key"], "the identity as the key, R = [1]B, S = 1: [S]B = R + [k]A for every digest, R of full order") + alias = bytes([0xEE] + [0xFF] * 30 + [0x7F]) + add("seal-key-noncanonical", rx(run, lambda r: r.update(key=alias.hex())), ["bad_key"], "a key whose y is p + 1: the identity, spelt a second way") + y = 2 + while _decode(y.to_bytes(32, "little")) is not None: + y += 1 + add("seal-key-off-curve", rx(run, lambda r: r.update(key=y.to_bytes(32, "little").hex())), ["bad_key"], "a key that is no point on the curve") + mixed = _encode(_add(_decode(public_key(DEMO)), _decode(ORDER8))) + add("seal-key-mixed-order", rx(run, lambda r: r.update(key=mixed.hex())), ["bad_key"], "the demo key plus a point of order 8: not of small order, outside the prime-order subgroup") + + def s_plus_l(r): + sig = bytes.fromhex(r["signature"]) + s = int.from_bytes(sig[32:], "little") + L + r["signature"] = (sig[:32] + s.to_bytes(32, "little")).hex() + add("seal-s-plus-l", rx(run, s_plus_l), ["bad_signature"], "S + l: a second spelling of the same signature") + # S made for this R by the holder of the key, so that [S]B = R + [k]A holds: + # only the rule on R can refuse it. a is the key's scalar; R is the identity + # (spelt one way or the other), so [S]B = [k]A is S = k·a. + def equation_holds(r_bytes): + def edit(r): + h = hashlib.sha512(DEMO).digest() + a = int.from_bytes(h[:32], "little") + a &= (1 << 254) - 8 + a |= 1 << 254 + k = int.from_bytes(hashlib.sha512(r_bytes + bytes.fromhex(r["key"]) + bytes.fromhex(r["digest"])).digest(), "little") % L + r["signature"] = (r_bytes + ((k * a) % L).to_bytes(32, "little")).hex() + return edit + add("seal-r-noncanonical", rx(run, equation_holds(alias)), ["bad_signature"], "R spelt with y of p or more") + add("seal-r-small-order", rx(run, equation_holds(IDENTITY)), ["bad_signature"], "R the identity, S made so that the equation holds: a lax check takes it") + add("seal-signature-other-key", rx(run, lambda r: r.update(signature=sign(OTHER, bytes.fromhex(r["digest"])).hex())), ["bad_signature"], "a signature by another key") + stale = copy.deepcopy(run) + stale["result"]["expectations"][0]["value"] = 0.5 + add("seal-digest-stale", stale, ["digest_mismatch"], "the result changed after it was sealed: the digest alone") + add("seal-digest-other", rx(run, lambda r: r.update(digest=blake3(b"another job").hex())), ["bad_signature", "digest_mismatch"], "a digest of something else, the signature over the first") + + # --- the first form + first = seal(run, form="first") + add("form-first", first, [], "a receipt of the first form, sealed over the compact text in the document's order") + exps = copy.deepcopy(run) + exps["result"]["x"] = [1e16, 1.5e300, 1e-7, 2.5e-6, 1e15, 0.00001] + add("form-first-exponents", seal(exps, form="first"), [], "a first-form receipt with numbers its writer wrote with an exponent, signed both ways (1e+16, 1e-7)") + big_first = copy.deepcopy(run) + big_first["request"]["seed"] = 2 ** 53 + 1 + add("form-first-big-integer", seal(big_first, form="first"), [], "a first-form receipt sealing 2^53 + 1 by its digits, as its writer wrote it: not inexact") + past = copy.deepcopy(run) + past["request"]["seed"] = Raw("18446744073709551617", float(2 ** 64)) + add("form-first-past-its-integers", seal(past, form="first"), ["inexact_number"], "a first-form receipt with 2^64 + 1 written: past the integers its writer kept by their digits, it sealed the double 2^64") + past["request"]["seed"] = Raw("18446744073709551616", float(2 ** 64)) + add("form-first-past-its-integers-exact", seal(past, form="first"), [], "the same with 2^64 written, which the double holds") + unformed = rx(seal(big_first, form="first"), lambda r: r.update(alg="rsa-pss")) + unformed["request"]["seed"] = Raw("9007199254740993", 2 ** 53 + 1) + add("form-none-inexact", unformed, ["inexact_number", "receipt_form"], "a receipt with no canon and another alg names no form, so it is not of the first form, and 2^53 + 1 is inexact") + add("form-first-relabelled", rx(first, lambda r: r.update(canon="rfc8785")), ["digest_mismatch"], "a first-form receipt relabelled as canonical") + add("form-canonical-relabelled", rx(run, lambda r: r.pop("canon")), ["digest_mismatch"], "a canonical receipt relabelled as the first form") + reordered = copy.deepcopy(first) + keys = list(reordered["result"]) + reordered["result"] = {k: reordered["result"][k] for k in reversed(keys[:-1])} + reordered["result"]["receipt"] = first["result"]["receipt"] + add("form-first-reordered", reordered, ["digest_mismatch"], "a first-form receipt with its result's members reordered") + respelt = copy.deepcopy(first) + respelt["result"]["expectations"][0]["value"] = Raw("1", 1) + add("form-first-respelt", respelt, ["digest_mismatch"], "a first-form receipt with 1.0 written 1, which its writer wrote 1.0") + + # --- the canonical text: what any reader and writer may do changes nothing + spelt = copy.deepcopy(run) + spelt["request"] = dict(reversed(list(spelt["request"].items()))) + spelt["request"]["shots"] = Raw("1.0e3", 1000) + spelt["result"]["expectations"][0]["value"] = Raw("1", 1) + t = dump(spelt) + "\n" + t = t.replace('"OPENQASM', '"\\u004fPENQASM', 1).replace('"qubits"', '"\\u0071ubits"', 1) + add("canon-respelt", t, [], "members reordered, numbers written otherwise (1.0e3, 1), names and strings with escapes") + strings = {"request": {"qasm": "OPENQASM 3.0;", "\U0001F600": "a key above the BMP", "": "sorts after it in UTF-16", "z": "tab\tnewline\ncontrol\u0001 slash/ del\u007f sep
 nonchar￿ quote\" backslash\\"}, + "result": {"method": "statevector", "accuracy": {"class": "exact"}}} + add("canon-strings", seal(strings), [], "strings and names that RFC 8785 writes its own way: UTF-16 order, escapes, characters left as they are") + numbers = {"request": {"qasm": "OPENQASM 3.0;"}, + "result": {"method": "statevector", "accuracy": {"class": "exact"}, + "x": [1e21, 1e-7, 1e-6, 0.1 + 0.2, 5e-324, 1.7976931348623157e308, -0.0, 123456789012345680000.0, 2 ** 53, 2 ** 64, 0, -1, 1e300, 4.35, 0.000001234]}} + sealed = seal(numbers) + sealed["result"]["x"][1] = Raw("0.0000001", 1e-7) + sealed["result"]["x"][9] = Raw("18446744073709551616", 2 ** 64) + add("canon-numbers", sealed, [], "numbers at the edges of ECMAScript's layout, and 2^64, a whole number a double holds") + tiny = seal({"request": {"qasm": "OPENQASM 3.0;"}, "result": {"method": "statevector", "accuracy": {"class": "exact"}, "x": 0}}) + tiny["result"]["x"] = Raw("1e-400", 0) + add("canon-underflow", tiny, [], "1e-400 reads as zero, as sealed") + near = seal({"request": {"qasm": "OPENQASM 3.0;"}, "result": {"method": "statevector", "accuracy": {"class": "exact"}, "x": 1.7976931348623157e308}}) + near["result"]["x"] = Raw("1.7976931348623158e308", 1.7976931348623157e308) + add("canon-near-max", near, [], "a number just below the limit, which rounds to the largest double") + inexact = seal({"request": {"qasm": "OPENQASM 3.0;"}, "result": {"method": "statevector", "accuracy": {"class": "exact"}, "x": 2 ** 53}}) + inexact["result"]["x"] = Raw("9007199254740993", 2 ** 53) + add("number-inexact", inexact, ["inexact_number"], "2^53 + 1 written, 2^53 sealed: the text says more than the seal") + big = seal({"request": {"qasm": "OPENQASM 3.0;", "seed": 2 ** 64}, "result": {"method": "statevector", "accuracy": {"class": "exact"}}}) + big["request"]["seed"] = Raw("18446744073709551617", 2 ** 64) + add("number-inexact-unsealed", rx(run, lambda r: r.update(x=Raw("9007199254740993", 2 ** 53 + 1))), [], "2^53 + 1 in a member of the receipt, which nothing seals and a verifier ignores") + add("number-inexact-past-u64", big, ["inexact_number"], "2^64 + 1 in the request, 2^64 sealed") + + # --- reading (section 8.2), each a text + base = dump(run) + "\n" + add("read-duplicate", base.replace('"qubits": ', '"qubits": 7,\n "qubits": ', 1), ["unreadable"], "a member of the result named twice") + add("read-duplicate-unsealed", base.replace('"status": "done"', '"status": "failed",\n "status": "done"', 1), ["unreadable"], "a member of the unsealed envelope named twice: still unreadable") + add("read-duplicate-escaped", base.replace('"qubits": ', '"qubit\\u0073": 7,\n "qubits": ', 1), ["unreadable"], "two names that are one once their escapes are read") + add("read-lone-surrogate", base.replace('"OPENQASM', '"\\ud800OPENQASM', 1), ["unreadable"], "an escaped high surrogate with no low one") + add("read-lone-low-surrogate", base.replace('"OPENQASM', '"\\udc00OPENQASM', 1), ["unreadable"], "an escaped low surrogate alone") + add("read-byte-order-mark", "" + base, ["unreadable"], "a byte order mark") + add("read-raw-control", base.replace('"OPENQASM', '"\tOPENQASM', 1), ["unreadable"], "a tab not escaped inside a string") + add("read-number-overflow", base.replace('"status": "done"', '"status": "done",\n "x": 1e400', 1), ["unreadable"], "a number no double reaches, in the unsealed envelope") + limit = "179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497792" + add("read-number-at-limit", base.replace('"status": "done"', '"status": "done",\n "x": ' + limit, 1), ["unreadable"], "2^1024 - 2^970 exactly, which rounds to infinity") + deep = {"request": {"qasm": "OPENQASM 3.0;", "nest": []}, "result": {"method": "statevector", "accuracy": {"class": "exact"}}} + n = deep["request"]["nest"] + for _ in range(124): + n.append([]) + n = n[0] + add("read-depth-127", seal(deep), [], "arrays and objects 127 deep, the most a reader takes") + n.append([]) + add("read-depth-128", seal(deep), ["unreadable"], "128 deep") + add("read-trailing", base + "{}\n", ["unreadable"], "a second value after the first") + add("read-not-json", "signature VALID\n", ["unreadable"], "not JSON") + add("read-not-utf8", base.encode("utf-8").replace(b"OPENQASM", b"\xffPENQASM", 1), ["unreadable"], "a byte that is not UTF-8") + add("job-array", "[]\n", ["not_a_job"], "a value that is not an object") + b = copy.deepcopy(run) + del b["request"] + add("job-no-request", b, ["not_a_job"], "no request") + b = copy.deepcopy(run) + b["request"] = "OPENQASM 3.0;" + add("job-request-string", b, ["not_a_job"], "a request that is not an object") + b = copy.deepcopy(run) + del b["result"]["receipt"] + add("job-no-receipt", b, ["no_receipt"], "a result with no receipt") + b = copy.deepcopy(run) + b.update(status="failed", method="a different method", why="anything at all", id="j-another") + add("envelope-changed", b, [], "the unsealed envelope changed: nothing a verifier takes") + + # --- a provider's result (section 8.4), each sealed again so one rule breaks + def at(path, value): + def f(b): + o = b + for k in path[:-1]: + o = o[k] + o[path[-1]] = value + return f + + def drop(path): + def f(b): + o = b + for k in path[:-1]: + o = o[k] + del o[path[-1]] + return f + + R = ("result",) + add("provider-measured-simulator", edited(prov, at(R + ("accuracy", "class"), "measured")), ["provider_class"], "a simulator's stated result labelled measured") + add("provider-approximate-false", edited(prov, at(R + ("accuracy", "approximate"), False)), ["provider_class"], "approximate given and not true") + + def silicon_approximate(b): + b["result"]["provider"]["provenance"] = "silicon" + b["result"]["accuracy"]["class"] = "measured" + b["result"]["accuracy"]["approximate"] = True + add("provider-approximate-processor", edited(prov, silicon_approximate), ["provider_class"], "a processor's counts labelled approximate") + add("provider-provenance-unknown", edited(prov, at(R + ("provider", "provenance"), "fpga")), ["provider_class", "provider_members"], "a provenance that is none of silicon, simulator or emulator") + add("provider-approximate-simulator", edited(prov, at(R + ("accuracy", "approximate"), True)), [], "a simulator's result its provider states is approximate") + add("provider-request-provider", edited(prov, at(("request", "provider"), "another")), ["request_provider"], "the request names another provider") + add("provider-request-shots", edited(prov, at(("request", "shots"), 301)), ["request_shots"], "the request asks for other shots") + add("provider-request-shots-default", edited(prov, drop(("request", "shots"))), ["request_shots"], "the request gives no shots (1024), the result others") + + def recount(f): + def g(b): + f(b["result"]) + return g + add("provider-counts-sum", edited(prov, recount(lambda r: r["counts"].update({k: v + 1 for k, v in list(r["counts"].items())[:1]}))), ["counts"], "counts that do not sum to the shots") + + def wider(r): + k, v = next(iter(r["counts"].items())) + del r["counts"][k] + r["counts"]["0" + k] = v + add("provider-counts-width", edited(prov, recount(wider)), ["counts"], "outcomes of two lengths") + add("provider-counts-zero", edited(prov, recount(lambda r: r["counts"].update({"01": 0}))), ["counts"], "an outcome listed with a count of 0") + + def hidden(r): + k, v = next(iter(r["counts"].items())) + r["counts"][k] = 1 + r["omitted_shots"] = v - 1 + # Section 8.4: only a count's value has a ceiling, and a sum is exact. + add("provider-whole-past-safe", edited(prov, at(R + ("job", "polls"), 2 ** 53)), [], "2^53 polls: a whole number has no ceiling but a count's") + add("provider-whole-spelt-large", edited(prov, at(R + ("provider", "qubits"), 1e30)), [], "1e30 qubits: whole, and absurd, and no rule's to refuse") + def past_safe(count): + def edit(r): + r["request"]["shots"] = 2 ** 53 + r["result"]["shots"] = 2 ** 53 + r["result"]["counts"] = {"00": count, "11": 2 ** 53 - count} if count < 2 ** 53 else {"00": count} + r["result"]["distinct_outcomes"] = len(r["result"]["counts"]) + r["result"]["omitted_shots"] = 0 + return edit + add("provider-shots-past-safe", edited(prov, past_safe(2 ** 53 - 1)), [], "2^53 shots, counted within a count's ceiling") + add("provider-count-past-safe", edited(prov, past_safe(2 ** 53)), ["counts"], "a count of 2^53, past its ceiling") + def same_strings(r): + r["request"]["shots"] = "300" + r["result"]["shots"] = "300" + add("provider-shots-same-strings", edited(prov, same_strings), ["counts"], "equal strings are the request's shots, and a string is no shots") + add("provider-counts-hidden", edited(prov, recount(hidden)), ["counts"], "shots left out while fewer than 65,536 outcomes are listed") + add("provider-counts-distinct", edited(prov, recount(lambda r: r.update(distinct_outcomes=r["distinct_outcomes"] + 1))), ["counts"], "distinct_outcomes not the outcomes listed") + add("provider-members-describe", edited(prov, at(R + ("provider", "describe"), prov["result"]["provider"]["describe"].upper())), ["provider_members"], "the describe hash in upper case") + add("provider-members-no-qasm3", edited(prov, drop(R + ("compiled", "qasm3"))), ["provider_members"], "no compiled program's text") + add("provider-members-energy", edited(prov, at(R + ("energy",), {"joules": 1})), ["provider_members"], "energy as a figure measured here") + add("provider-members-kwh-unlabelled", edited(prov, at(R + ("energy", "service_kwh"), 0.001)), ["provider_members"], "the provider's energy figure not labelled as its own") + add("provider-members-no-schema", edited(prov, drop(R + ("schema",))), ["provider_members"], "no schema") + add("provider-members-no-charged", edited(prov, drop(R + ("cost", "charged"))), ["provider_members"], "no charged") + add("provider-members-basis", edited(prov, at(R + ("provider", "basis"), "gpizz")), ["provider_members"], "the provider's basis not the program's") + add("provider-counts-source-other", edited(prov, at(R + ("counts_source",), "estimated")), ["counts_source"], "a counts_source of no value") + add("provider-execution-default", edited(prov, at(R + ("execution", "twirling"), "default")), ["execution"], "a provider saying default, which only a client says") + add("provider-execution-verbatim-remapped", edited(prov, at(R + ("execution", "remapping"), "on")), ["execution"], "run as written and remapped") + add("provider-execution-sent", edited(prov, at(R + ("execution", "sent"), {"fields": [], "settings": {}})), ["execution"], "sent, which only a client states") + add("provider-execution-member", edited(prov, at(R + ("execution", "routing"), "off")), ["execution"], "a member execution does not have") + nobody = {"stated_by": "nobody", "mitigation": "unknown", "twirling": "unknown", "decoupling": "unknown", "remapping": "unknown", "optimisation": "unknown", "verbatim": None} + add("provider-execution-nobody", edited(prov, at(R + ("execution",), nobody)), [], "a provider that states nothing: no one's word") + client = {"stated_by": "client", "sent": {"fields": ["op", "program", "protocol", "shots"], "settings": {}}, "mitigation": "default", "twirling": "default", "decoupling": "default", "remapping": "default", "optimisation": "default", "verbatim": False} + add("provider-execution-client-open", edited(prov, at(R + ("execution",), client)), ["execution"], "the open protocol's client claiming to state what was set") + add("provider-compiled-program", edited(prov, at(R + ("compiled", "qasm3"), prov["result"]["compiled"]["qasm3"] + "// another program\n")), ["compiled_program"], "a program's text that is not the text its hash names") + add("provider-cost-free-confirmed", edited(prov, at(R + ("cost", "confirmed"), True)), ["cost_confirmation"], "a free run that claims a confirmation") + add("provider-cost-unconfirmed", edited(prov, at(R + ("cost", "free"), False)), ["cost_confirmation"], "a run that was not free, with no confirmation") + add("provider-stated-by-a-null", edited(legacy, at(R + ("counts_source",), None)), ["counts_source", "execution", "provider_class", "provider_members"], "an earlier-form result given counts_source null: stated by its members, whatever their values") + add("earlier-request-shots", edited(legacy, at(("request", "shots"), 65)), ["request_shots"], "an earlier-form result whose request asks other shots") + add("earlier-approximate", edited(legacy, at(R + ("accuracy", "approximate"), True)), ["provider_class"], "approximate on an earlier-form result") + add("earlier-simulated", edited(legacy, at(R + ("accuracy", "class"), "simulated")), ["provider_class"], "the earlier form labelled simulated, which no earlier engine wrote") + + # --- what each built-in kind records (section 8.3) + add("kind-sampler-twirling-default", edited(sampler, at(R + ("execution", "twirling"), "default")), ["execution"], "sampler-v2 not stating the twirling it sends off") + add("kind-prx-shots", edited(prx, at(R + ("counts_source",), "shots")), ["counts_source"], "prx-circuits claiming shot records it does not read") + add("kind-prx-twirling-off", edited(prx, at(R + ("execution", "twirling"), "off")), ["execution"], "prx-circuits claiming a setting it does not send") + add("kind-turns-shots", edited(turns, at(R + ("counts_source",), "shots")), ["counts_source"], "native-turns claiming shots: its counts are always rebuilt from probabilities") + add("kind-tasks-vendor-counts", edited(qpu, at(R + ("counts_source",), "vendor_counts")), ["counts_source"], "cloud-tasks claiming the provider's own counts") + add("kind-tasks-simulator-boxed", edited(sim, at(R + ("execution",), {k: v for k, v in qpu["result"]["execution"].items()})), ["execution"], "a simulator's task stating the processor's verbatim box") + def nobody(r): + r["result"]["execution"] = dict({k: "unknown" for k in ("mitigation", "twirling", "decoupling", "remapping", "optimisation")}, stated_by="nobody", verbatim=None) + r["result"]["compiled"].pop("qubit_names") + # Section 8.4: compiled.qubit_names. + add("kind-tasks-no-names", edited(qpu, drop(R + ("compiled", "qubit_names"))), ["provider_members"], "cloud-tasks, stated by its client, not saying which of the device's qubits its indices were") + add("kind-prx-no-names", edited(prx, drop(R + ("compiled", "qubit_names"))), ["provider_members"], "prx-circuits, stated by its client, not saying which of the device's qubits its indices were") + add("kind-tasks-names-null", edited(qpu, at(R + ("compiled", "qubit_names"), None)), ["provider_members"], "null is given, and is no object") + add("kind-tasks-name-leading-zero", edited(qpu, at(R + ("compiled", "qubit_names"), dict(qpu["result"]["compiled"]["qubit_names"], **{"01": "x"}))), ["provider_members"], "an index with a leading zero") + add("kind-sampler-names-short", edited(sampler, at(R + ("compiled", "qubit_names"), {})), ["provider_members"], "names given where they are optional, and not of every index of the layouts") + add("kind-prx-nobody", edited(prx, nobody), [], "a job recorded before its client said what it sent: stated by nobody, on a kind with rows") + add("kind-tasks-processor-unboxed", edited(qpu, at(R + ("execution", "verbatim"), False)), ["execution"], "a processor's task not stating its verbatim box") + + # Section 8.4: every index qasm3 acts on is named. + def acts_on_one_more(r): + c = r["result"]["compiled"] + q = max(int(k) for k in c["qubit_names"]) + 1 + c["qasm3"] = c["qasm3"] + "x $%d;\n" % q + c["program"] = blake3(c["qasm3"].encode("utf-8")).hex() + add("kind-tasks-names-short-of-text", edited(qpu, acts_on_one_more), ["provider_members"], "qasm3 acts on a qubit its names do not name, outside both layouts") + + # --- a whole double past 2^53, as RFC 8785 writes it (section 8.2) + def big_job(spelling): + # Each spelling stands for the double it reads as (section 8.2). + return {"request": {"qasm": "OPENQASM 3.0;", "observables": [{"pauli": "Z0", "coeff": Raw(spelling, float(spelling))}]}, + "result": {"method": "statevector", "accuracy": {"class": "exact"}, "x": Raw(spelling, float(spelling))}} + add("number-canonical-spelling", seal(big_job("1152921504606847000")), [], "2^60 written as RFC 8785 writes it, its shortest digits and zeros: the double's own spelling") + add("number-exact-spelling", seal(big_job("1152921504606846976")), [], "2^60 written by its exact digits") + add("number-exponent-spelling", seal(big_job("1.152921504606847e+18")), [], "2^60 written with an exponent, as Python writes it") + add("number-near-canonical", seal(big_job("1152921504606847001")), ["inexact_number"], "digits one past the double's canonical text: another spelling of an inexact whole number") + add("number-padded-past", seal(big_job("1152921504606847000000")), ["inexact_number"], "the digits padded past 10^21, where RFC 8785 writes an exponent: not its spelling") + + # --- sets (section 8.6) + S = ("result",) + add("set-error-item", seal_set(edited_set(progs, lambda r: (r["items"][1].pop("result"), r["items"][1].update(error="it did not run")))), [], "a set whose second item failed: its error is its leaf") + add("set-item-changed", seal_set(edited_set(progs, lambda r: r["items"][0]["result"]["counts"].update({"00": r["items"][0]["result"]["counts"].get("00", 0) + 1}))), ["set_item"], "an item's counts changed after it was sealed, the set sealed again over it") + add("set-root-other", edited_set_head(progs, lambda r: r.update(root="ab" * 32)), ["set_root"], "a root that is no tree's of these items, sealed by the set") + add("set-path-other", edited_set(progs, lambda r: r["items"][0]["path"].__setitem__(0, "cd" * 32)), ["set_root"], "an item's path changed: a path is not sealed, and must lead to the root") + add("set-count-other", edited_set_head(progs, lambda r: r.update(count=r["count"] + 1)), ["set_items"], "a count other than the items'") + add("set-index-swapped", edited_set(progs, lambda r: (r["items"][0].update(index=1), r["items"][1].update(index=0))), ["set_items"], "items whose index is not their place") + def other_shots(r): + it = r["items"][1] + it["request"]["shots"] = 65 + sealed = seal({"request": it["request"], "result": it["result"]}) + it["result"] = sealed["result"] + add("set-item-not-given", seal_set(edited_set(progs, other_shots)), ["set_items"], "an item sealed alone, and not the item the request's programs give") + add("set-head-changed", edited_set(progs, lambda r: r.update(elapsed_ms=r["elapsed_ms"] + 1)), ["digest_mismatch"], "the head changed after the set was sealed") + def failed_inexact(r): + it = r["items"][1] + it.pop("result") + it["error"] = "it did not run" + it["request"]["seed"] = Raw("9007199254740993", 2 ** 53 + 1) + add("set-error-item-inexact", seal_set(edited_set(progs, failed_inexact)), ["set_item", "set_items"], "a failed item whose request writes a number its leaf seals as another: no item's number escapes") + def first_form_set(body): + """A set sealed in the first form: its receipt over its request and head by its writer's text.""" + b = copy.deepcopy(body) + head = {k: v for k, v in b["result"].items() if k not in ("items", "receipt")} + d = digest(b["request"], head, form="first") + b["result"]["receipt"] = {"alg": "ed25519", "key": public_key(DEMO).hex(), "digest": d.hex(), "signature": sign(DEMO, d).hex()} + return b + def failed_by_digits(r): + it = r["items"][1] + it.pop("result") + it["error"] = "it did not run" + failed = seal_set(edited_set(progs, failed_by_digits)) + add("set-first-form-error-item", first_form_set(failed), [], "a set of the first form, an item failed: valid") + by_digits = copy.deepcopy(failed) + by_digits["result"]["items"][1]["request"]["seed"] = Raw("9007199254740993", float(2 ** 53 + 1)) + add("set-first-form-error-item-inexact", first_form_set(seal_set(by_digits)), ["set_item", "set_items"], "a set of the first form whose failed item writes 2^53 + 1 by its digits: its leaf, the canonical form's digest, seals the double") + def stranger(r): + it = r["items"][0] + it["result"] = seal({"request": it["request"], "result": it["result"]}, seed=OTHER)["result"] + add("set-item-other-key", seal_set(edited_set(progs, stranger)), ["set_item"], "an item sealed by a key other than the set's") + add("set-empty", edited_set_head(progs, lambda r: (r["items"].clear(), r.update(count=0, root="00" * 32))), ["set_items"], "a set of no items, its head sealed so") + add("set-sweep-short", seal_set(edited_set(sweep, lambda r: (r["items"].pop(), r.update(count=len(r["items"]))))), ["set_items"], "a sweep missing its last point, counted and sealed so") + big_sweep = copy.deepcopy(sweep) + big_sweep["request"]["sweep"]["theta"] = [Raw("9007199254740993", 2 ** 53 + 1)] + big_sweep["request"]["sweep"]["theta"][1:] + add("set-sweep-inexact", seal_set(big_sweep), ["inexact_number"], "a sweep's value no double holds, in the set's own request") + + # --- another method claims a provider's class + add("method-measured", edited(run, at(R + ("accuracy", "class"), "measured")), ["provider_class"], "the engine's own result labelled measured") + add("method-simulated", edited(run, at(R + ("accuracy", "class"), "simulated")), ["provider_class"], "the engine's own result labelled simulated") + add("method-class-unknown", edited(run, at(R + ("accuracy", "class"), "quasi-exact")), [], "a class a verifier does not know: not a reason, since a later version may add one") + return out + + +# --------------------------------------------------------------------- the made half + +MADE = { + "cli-run-observables.json": ("the command line, on this machine, with --demo-key: a Bell pair's expectation values", []), + "cli-run-statevector.json": ("the command line: a three-qubit circuit's counts by the state vector", []), + "cli-run-trajectories.json": ("the command line: noisy trajectories, an estimate with its error", []), + "cli-run-mps.json": ("the command line: thirty qubits as a matrix product state", []), + "cli-compile.json": ("the command line: a compile, its certificate and its receipt", []), + "cli-provider-reference.json": ("the command line, on the reference provider over a local process: a simulator's stated result", []), + "cli-provider-reference-priced.json": ("the command line, on the reference provider with a price, confirmed with --yes", []), + "service-run.json": ("the free job service, signed with its key", []), + "service-run-stabilizer.json": ("the free job service: a stabilizer job with observables", []), + "scripted-sampler-v2.json": ("sampler-v2 through a service that follows its contract", []), + "scripted-prx-circuits.json": ("prx-circuits through a service that follows its contract", []), + "scripted-native-turns.json": ("native-turns, its simulator, through a service that follows its contract", []), + "scripted-native-turns-noise.json": ("native-turns, its simulator under a device's noise model", []), + "scripted-cloud-tasks-processor.json": ("cloud-tasks on a processor: a verbatim box, rows of shots", []), + "scripted-cloud-tasks-processor-probabilities.json": ("cloud-tasks on a processor whose result holds probabilities: counts rebuilt", []), + "scripted-cloud-tasks-simulator.json": ("cloud-tasks on its simulator: simulated", []), + "fixture-job-legacy.json": ("a run sealed before results named their engine and schema (the engine's pinned fixture)", []), + "fixture-provider-job-legacy.json": ("a provider job of the earlier form: a simulator's counts sealed as measured, read as simulated", []), + "fixture-compile-legacy.json": ("a compile as an earlier page sealed it (the engine's pinned fixture)", []), + "cli-set-programs.json": ("the command line: a set of two programs, one receipt over both and each its own (section 8.6)", []), + "cli-set-sweep.json": ("the command line: a sweep of an input at three points, sealed as a set", []), + "first-form-service-run.json": ("the job service as it was on 18 October 2026, commit 72d18742d, sealing with the first form's own writer: counts", []), + "first-form-service-observables.json": ("the same service, the same writer: expectation values, doubles in its own text", []), +} + + +def manifest(crafted): + entries = [] + for name in sorted(MADE): + what, reasons = MADE[name] + entries.append({"file": "jobs/" + name, "reasons": reasons, "made_by": "made", "what": what}) + for name, _, reasons, what in crafted: + entries.append({"file": "jobs/" + name, "reasons": reasons, "made_by": "crafted", "what": what}) + return { + "about": "Job receipts and the reasons a verifier must give for each (wai/extensions/quantum-providers.md section 8.4): valid when there are none. made: sealed by the command line, the job service, the provider clients or an earlier engine, kept as made. crafted: made by generate.py from those, each changed in one way and sealed again with the published demo key where the change is inside the seal. Every expectation is written by hand from section 8.4.", + "entries": entries, + } + + +LOCK = os.path.join(HERE, "made.lock.json") + + +def lock_of(): + try: + with open(LOCK, encoding="utf-8") as f: + return json.load(f) + except OSError: + return {} + + +def digest_of(name): + with open(os.path.join(JOBS, name), "rb") as f: + return hashlib.sha256(f.read()).hexdigest() + + +def write_lock(lock): + with open(LOCK, "w", encoding="utf-8") as f: + f.write(json.dumps(lock, indent=2, sort_keys=True) + "\n") + + +def check_made(): + """A made receipt is kept: named in MADE, in jobs/, and the bytes pinned.""" + lock, problems = lock_of(), [] + for name in sorted(set(MADE) | set(lock)): + if name not in MADE: + problems.append("%s is pinned, and no longer in MADE: a made receipt is kept" % name) + elif name not in lock: + problems.append("%s is in MADE, and not pinned: generate.py --pin %s" % (name, name)) + elif not os.path.exists(os.path.join(JOBS, name)): + problems.append("%s is pinned, and gone from jobs/: a made receipt is kept" % name) + elif digest_of(name) != lock[name]: + problems.append("%s has changed since it was pinned: a made receipt is kept as it was made, and a new one takes a new name" % name) + on_disk = {n for n in os.listdir(JOBS) if n.endswith(".json") and not n.startswith("crafted-")} + for name in sorted(on_disk - set(MADE)): + problems.append("%s is in jobs/, and not in MADE" % name) + return problems + + +def pin(names): + lock = lock_of() + for name in names: + if name in lock: + sys.exit("generate.py --pin: %s is pinned already: a made receipt is kept as it was made" % name) + if name not in MADE: + sys.exit("generate.py --pin: %s is not in MADE" % name) + lock[name] = digest_of(name) + write_lock(lock) + print("generate.py --pin: %d pinned, %d in all" % (len(names), len(lock))) + + +def self_test(): + """The pins can fail: a made receipt dropped, changed, or unnamed, in a copy.""" + global JOBS, LOCK, MADE + import shutil + import tempfile + jobs, lock, made = JOBS, LOCK, MADE + failures = 0 + with tempfile.TemporaryDirectory() as tmp: + try: + JOBS, LOCK = os.path.join(tmp, "jobs"), os.path.join(tmp, "made.lock.json") + os.mkdir(JOBS) + for name in made: + shutil.copy(os.path.join(jobs, name), JOBS) + shutil.copy(lock, LOCK) + name = sorted(made)[0] + def expect(what, want): + nonlocal failures + problems = check_made() + held = any(want in p for p in problems) + print(("PASS " if held else "FAIL ") + what) + failures += not held + if check_made(): + print("FAIL the copy holds as it is: %s" % check_made()) + failures += 1 + with open(os.path.join(JOBS, name), "ab") as f: + f.write(b" ") + expect("a made receipt changed is found", "has changed") + os.remove(os.path.join(JOBS, name)) + expect("a made receipt dropped is found", "gone from jobs/") + MADE = {k: v for k, v in made.items() if k != name} + expect("a made receipt dropped from MADE too is found", "no longer in MADE") + MADE = dict(made) + shutil.copy(os.path.join(jobs, name), JOBS) + with open(os.path.join(JOBS, "made-by-no-one.json"), "wb") as f: + f.write(b"{}") + expect("a receipt in jobs/ that MADE does not name is found", "not in MADE") + os.remove(os.path.join(JOBS, "made-by-no-one.json")) + MADE = dict(made, **{"new.json": ("a new one", [])}) + expect("a name in MADE that is not pinned is found", "not pinned") + finally: + JOBS, LOCK, MADE = jobs, lock, made + if failures: + sys.exit(1) + print("generate.py --self-test: the pins can fail, each way") + + +def main(): + if sys.argv[1:2] == ["--pin"]: + return pin(sys.argv[2:]) + if sys.argv[1:] == ["--self-test"]: + return self_test() + check = "--check" in sys.argv[1:] + problems = check_made() + if problems: + for p in problems: + print("generate.py: " + p) + sys.exit(1) + crafted = cases() + want = {os.path.join(JOBS, name): (t if isinstance(t, bytes) else t.encode("utf-8")) for name, t, _, _ in crafted} + want[os.path.join(HERE, "manifest.json")] = (json.dumps(manifest(crafted), indent=2, ensure_ascii=False) + "\n").encode("utf-8") + stale = [n for n in os.listdir(JOBS) if n.startswith("crafted-") and os.path.join(JOBS, n) not in want] + bad = [] + for path, data in sorted(want.items()): + if check: + try: + with open(path, "rb") as f: + if f.read() != data: + bad.append(path) + except OSError: + bad.append(path) + else: + with open(path, "wb") as f: + f.write(data) + if check: + for p in bad + [os.path.join(JOBS, n) for n in stale]: + print("generate.py --check: %s is not what generate.py writes" % os.path.relpath(p, HERE)) + if bad or stale: + sys.exit(1) + print("generate.py --check: %d crafted receipts and the manifest are as written" % len(crafted)) + else: + for n in stale: + os.remove(os.path.join(JOBS, n)) + print("generate.py: wrote %d crafted receipts and the manifest" % len(crafted)) + + +if __name__ == "__main__": + main() diff --git a/wai/quantum-conformance/receipts/jobs/cli-compile.json b/wai/quantum-conformance/receipts/jobs/cli-compile.json new file mode 100644 index 000000000..54b717277 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/cli-compile.json @@ -0,0 +1 @@ +{"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[1] q;\nrx(0.4) q[0];\n","epsilon":1e-6,"level":1,"basis":"clifford_t"},"result":{"ok":true,"options":{"epsilon":1e-6,"level":1,"basis":"clifford_t"},"qasm":"OPENQASM 3.0;\nqubit[1] q;\nbit[1] c;\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nh q[0];\nc[0] = measure q[0];\n","certificate":{"format":"wai.quantum.compile/1","basis":"clifford_t","level":1,"source":{"hash":"726bc51921f883d8a97a4b491acc98558563224d8126abb34c246dfb06795917","qubits":1,"counts":{"gates":1,"one_qubit":1,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":1}},"output":{"hash":"3eb520195d7114991c2e86194ed7e08b1ed89fe2089256251aa4387e8515f76b","counts":{"gates":147,"one_qubit":147,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":59,"rotations":0,"depth":147}},"epsilon":{"budget":1e-6,"rotations":1,"each":1e-6,"bound":4.806804249103834e-7},"passes":[{"pass":"simplify","before":{"gates":1,"one_qubit":1,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":1},"after":{"gates":1,"one_qubit":1,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":1},"check":{"how":"rewrites, each checked on the qubits it touches by tableau or on every basis state","verdict":"exact","steps":0}},{"pass":"lower","before":{"gates":1,"one_qubit":1,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":1},"after":{"gates":3,"one_qubit":3,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":3},"check":{"how":"each kind of gate against its decomposition, on its own qubits","verdict":"exact","checked":1,"distance":2.237726045655905e-16}},{"pass":"simplify","before":{"gates":3,"one_qubit":3,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":3},"after":{"gates":1,"one_qubit":1,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":1},"check":{"how":"rewrites, each checked on the qubits it touches by tableau or on every basis state","verdict":"exact","steps":1}},{"pass":"phase_fold","before":{"gates":1,"one_qubit":1,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":1},"after":{"gates":1,"one_qubit":1,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":1},"check":{"how":"the phase polynomial recomputed: the same gates once the phases are taken out, and the same angle on every parity, in whole eighths of a turn","verdict":"proof","merged":0}},{"pass":"lower","before":{"gates":1,"one_qubit":1,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":1},"after":{"gates":3,"one_qubit":3,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":3},"check":{"how":"each kind of gate against its decomposition, on its own qubits","verdict":"exact","checked":1,"distance":2.237726045655905e-16}},{"pass":"approximate","before":{"gates":3,"one_qubit":3,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":0,"rotations":1,"depth":3},"after":{"gates":147,"one_qubit":147,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":59,"rotations":0,"depth":147},"check":{"how":"each rotation's Clifford+T word multiplied out in exact ring arithmetic, and its operator-norm distance to the rotation recomputed from the word in fixed point and rounded up","verdict":"approximate","rotations":1,"epsilon_each":1e-6,"t_count":59,"distance":4.806804249103834e-7}},{"pass":"phase_fold","before":{"gates":147,"one_qubit":147,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":59,"rotations":0,"depth":147},"after":{"gates":147,"one_qubit":147,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":59,"rotations":0,"depth":147},"check":{"how":"the phase polynomial recomputed: the same gates once the phases are taken out, and the same angle on every parity, in whole eighths of a turn","verdict":"proof","merged":26}},{"pass":"simplify","before":{"gates":147,"one_qubit":147,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":59,"rotations":0,"depth":147},"after":{"gates":61,"one_qubit":61,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":59,"rotations":0,"depth":61},"check":{"how":"rewrites, each checked on the qubits it touches by tableau or on every basis state","verdict":"exact","steps":56}},{"pass":"lower","before":{"gates":61,"one_qubit":61,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":59,"rotations":0,"depth":61},"after":{"gates":147,"one_qubit":147,"two_qubit":0,"three_qubit":0,"cnots":0,"t_count":59,"rotations":0,"depth":147},"check":{"how":"each kind of gate against its decomposition, on its own qubits","verdict":"exact","checked":3,"distance":2.4729823389985603e-16}}],"rotations":[{"angle":0.4,"t_count":59,"distance":4.806804249103834e-7,"k":30}],"whole":{"how":"the dense unitaries of the whole program and the whole output","distance":4.806804251701884e-7,"verdict":"approximate"},"verdict":"approximate","reading":"The output equals the program up to a global phase to within 4.806804249103834e-7 in the operator norm (the budget was 1e-6). On any input, the output distribution is then within bound of the program's in total variation (so is every outcome's probability), and an observable's expectation within 2·bound·‖O‖. Hashes are BLAKE3 of the program as read (written out as OpenQASM 3) and of the output text."},"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.compile-result/1","receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"80bc6010959ffd9bd2e5f20fc0c940ccf8c909e9c16e5dec497a9826f33ee115","signature":"36a818dd20d3cf47321f04d29547223e580c786b132bc59e69aabedb39fb2bb68ced06720da70fc799e96735beb0a6b2d818f3a61682f48ff9b50c9f01204f0c"}}} diff --git a/wai/quantum-conformance/receipts/jobs/cli-provider-reference-priced.json b/wai/quantum-conformance/receipts/jobs/cli-provider-reference-priced.json new file mode 100644 index 000000000..c95ed22d1 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/cli-provider-reference-priced.json @@ -0,0 +1 @@ +{"status":"done","method":"provider","why":"on priced, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent","request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n","shots":300,"seed":4,"provider":"priced"},"result":{"method":"provider","qubits":2,"gates":2,"accuracy":{"class":"simulated","note":"counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would"},"shots":300,"provider":{"name":"priced","kind":"process","protocol":"wai.quantum.provider/1","device":"reference:line-5","provenance":"simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given","qubits":5,"basis":"rzsxxcz","describe":"c7626c0d9f68fe8e420df9cfe19f635427160bfe4902af2cc12c480b28d3b439"},"calibration":"reference-f2d9fe570421","compiled":{"basis":"rzsxxcz","program":"b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9","hash":"BLAKE3 of the program's OpenQASM 3 text, qasm3","qasm3":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n","native_gates":9,"two_qubit_gates":1,"swaps":0,"layout":[0,1],"final_layout":[0,1],"checked":"the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent","placement":{"chosen":"the compiler's own layout, the fewest swaps: the description states no errors"}},"job":{"id":"ref-1","polls":1},"ideal":{"computed":true,"method":"exact","total_variation_distance":0.03666666666666665,"hellinger_fidelity":0.998653743148044,"sampling_tvd_expected":0.023032943298089033,"ideal_outcomes":2,"note":"the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent"},"cost":{"free":false,"estimate":{"free":false,"amount":"0.09","currency":"USD","unit":"shot","seconds":0.0},"charged":{"amount":"0.09","currency":"USD"},"confirmed":true},"energy":{"unmetered":"the work ran at the provider; this host's meter would read only the wait"},"counts_source":"shots","execution":{"stated_by":"provider","mitigation":"none","twirling":"none","decoupling":"none","remapping":"none","optimisation":"none","verbatim":true,"note":"the reference simulates the program's gates as written, on the qubits named, and corrects nothing"},"counts":{"11":161,"00":139},"distinct_outcomes":2,"omitted_shots":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"a9f7cd574d721a9a2cd9178cf38b25cd2081b6871d378bab70394fcb1861228b","signature":"7b150fc6c183213f2c776869e306016b31e586c1fe8ec702e9e9fd009a0c51a7366a25c0422a2169ef627682ff72917cb84cb693f53acec52bf24786daa5f500"}},"id":"pj-9a09dd19b51481e8","submitted_ms":1793907034821,"finished_ms":1793907034821} diff --git a/wai/quantum-conformance/receipts/jobs/cli-provider-reference.json b/wai/quantum-conformance/receipts/jobs/cli-provider-reference.json new file mode 100644 index 000000000..ad9202844 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/cli-provider-reference.json @@ -0,0 +1 @@ +{"status":"done","method":"provider","why":"on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent","request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n","shots":300,"seed":4,"provider":"ref"},"result":{"method":"provider","qubits":2,"gates":2,"accuracy":{"class":"simulated","note":"counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would"},"shots":300,"provider":{"name":"ref","kind":"process","protocol":"wai.quantum.provider/1","device":"reference:line-5","provenance":"simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given","qubits":5,"basis":"rzsxxcz","describe":"0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0"},"calibration":"reference-f2d9fe570421","compiled":{"basis":"rzsxxcz","program":"b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9","hash":"BLAKE3 of the program's OpenQASM 3 text, qasm3","qasm3":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n","native_gates":9,"two_qubit_gates":1,"swaps":0,"layout":[0,1],"final_layout":[0,1],"checked":"the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent","placement":{"chosen":"the compiler's own layout, the fewest swaps: the description states no errors"}},"job":{"id":"ref-1","polls":1},"ideal":{"computed":true,"method":"exact","total_variation_distance":0.03666666666666665,"hellinger_fidelity":0.998653743148044,"sampling_tvd_expected":0.023032943298089033,"ideal_outcomes":2,"note":"the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent"},"cost":{"free":true,"estimate":{"free":true,"seconds":0.0},"charged":null,"confirmed":false},"energy":{"unmetered":"the work ran at the provider; this host's meter would read only the wait"},"counts_source":"shots","execution":{"stated_by":"provider","mitigation":"none","twirling":"none","decoupling":"none","remapping":"none","optimisation":"none","verbatim":true,"note":"the reference simulates the program's gates as written, on the qubits named, and corrects nothing"},"counts":{"11":161,"00":139},"distinct_outcomes":2,"omitted_shots":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"0e568337eb3ba31b51bf82a2329353632aaed80d96ab197ec6c27a8e4376efb5","signature":"eb0a2972e4e8a587cfd387001c25604f0723d74000b88c9e5be540bb8ed374e9f809035dd34c2ed6ac65dee4793622a59e55d2a16b6affcbb4cfe872b3b52a09"}},"id":"pj-442c6043319912f8","submitted_ms":1793907034817,"finished_ms":1793907034817} diff --git a/wai/quantum-conformance/receipts/jobs/cli-run-mps.json b/wai/quantum-conformance/receipts/jobs/cli-run-mps.json new file mode 100644 index 000000000..5f3c22766 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/cli-run-mps.json @@ -0,0 +1 @@ +{"status":"done","method":"mps","why":"matrix product state of 30 qubits at bond dimension 64","estimate":{"memory_bytes":277872640,"work":65732608},"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[30] q;\nbit[30] c;\nrx(0.3) q[0];\ncx q[0], q[1];\nrx(0.3) q[1];\ncx q[1], q[2];\nrx(0.3) q[2];\ncx q[2], q[3];\nrx(0.3) q[3];\ncx q[3], q[4];\nrx(0.3) q[4];\ncx q[4], q[5];\nrx(0.3) q[5];\ncx q[5], q[6];\nrx(0.3) q[6];\ncx q[6], q[7];\nrx(0.3) q[7];\ncx q[7], q[8];\nrx(0.3) q[8];\ncx q[8], q[9];\nrx(0.3) q[9];\ncx q[9], q[10];\nrx(0.3) q[10];\ncx q[10], q[11];\nrx(0.3) q[11];\ncx q[11], q[12];\nrx(0.3) q[12];\ncx q[12], q[13];\nrx(0.3) q[13];\ncx q[13], q[14];\nrx(0.3) q[14];\ncx q[14], q[15];\nrx(0.3) q[15];\ncx q[15], q[16];\nrx(0.3) q[16];\ncx q[16], q[17];\nrx(0.3) q[17];\ncx q[17], q[18];\nrx(0.3) q[18];\ncx q[18], q[19];\nrx(0.3) q[19];\ncx q[19], q[20];\nrx(0.3) q[20];\ncx q[20], q[21];\nrx(0.3) q[21];\ncx q[21], q[22];\nrx(0.3) q[22];\ncx q[22], q[23];\nrx(0.3) q[23];\ncx q[23], q[24];\nrx(0.3) q[24];\ncx q[24], q[25];\nrx(0.3) q[25];\ncx q[25], q[26];\nrx(0.3) q[26];\ncx q[26], q[27];\nrx(0.3) q[27];\ncx q[27], q[28];\nrx(0.3) q[28];\ncx q[28], q[29];\nc = measure q;\n","shots":20,"seed":1,"method":"mps"},"result":{"method":"mps","qubits":30,"gates":58,"accuracy":{"class":"exact","max_bond":64,"max_bond_used":2,"note":"no truncation dropped weight beyond the cutoff's rounding"},"counts":{"000000000000000000000000000000":7,"111111111111110000000000000000":2,"000000111111111000000000000000":1,"000000111111111111111111111111":1,"000011111111111111111110000000":1,"000111111111111111111111111100":1,"111100000000000000000000000000":1,"111111100000000000000000000000":1,"111111111111111000000000000000":1,"111111111111111100000000000000":1,"111111111111111111100000000000":1,"111111111111111111111000000000":1,"111111111111111111111100000000":1},"distinct_outcomes":13,"omitted_shots":0,"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","energy":{"unmetered":"the command line does not meter a run's energy"},"receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"c23c14e79974526d6a405c4331ea3d6a99491319f070055b56f116e16a9fc0e6","signature":"c0f24e266f9c36ef4d953e4dbc00d8b028453aec138e70fc72f01df3638db1a2a6e8eac039bb314be0d33f234a857d40a5406ec8182403a27e5620f177994207"}}} diff --git a/wai/quantum-conformance/receipts/jobs/cli-run-observables.json b/wai/quantum-conformance/receipts/jobs/cli-run-observables.json new file mode 100644 index 000000000..4dac1cec4 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/cli-run-observables.json @@ -0,0 +1 @@ +{"status":"done","method":"stabilizer","why":"Clifford circuit: the stabilizer tableau of 2 qubits","estimate":{"memory_bytes":67172870,"work":2016},"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n","shots":1000,"seed":7,"observables":["Z0 Z1","X0"]},"result":{"method":"stabilizer","qubits":2,"gates":2,"accuracy":{"class":"exact"},"counts":{"11":505,"00":495},"distinct_outcomes":2,"omitted_shots":0,"outcome_dimension":1,"expectations":[{"observable":"Z0 Z1","coeff":1.0,"value":1.0},{"observable":"X0","coeff":1.0,"value":0.0}],"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","energy":{"unmetered":"the command line does not meter a run's energy"},"receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1","signature":"7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306"}}} diff --git a/wai/quantum-conformance/receipts/jobs/cli-run-statevector.json b/wai/quantum-conformance/receipts/jobs/cli-run-statevector.json new file mode 100644 index 000000000..106380292 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/cli-run-statevector.json @@ -0,0 +1 @@ +{"status":"done","method":"statevector","why":"dense double-precision state vector of 3 qubits","estimate":{"memory_bytes":268447588,"work":40},"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n","shots":500,"seed":3,"method":"statevector"},"result":{"method":"statevector","qubits":3,"gates":5,"accuracy":{"class":"exact","fusion":3},"counts":{"000":252,"111":248},"distinct_outcomes":2,"omitted_shots":0,"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","energy":{"unmetered":"the command line does not meter a run's energy"},"receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"12938ced9f32eddf67182b3dcd112b3f80fdababda46f3435d004f3bf4ec84f9","signature":"b0fbdd938321ce7bc12e4fed53816aeca63e290b04cf615664bd9e83909107908a1075b5566f3e7ad2f78aa917cbc2431ff73bf01c3f117d8614327d3cd8ad06"}}} diff --git a/wai/quantum-conformance/receipts/jobs/cli-run-trajectories.json b/wai/quantum-conformance/receipts/jobs/cli-run-trajectories.json new file mode 100644 index 000000000..7f2101794 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/cli-run-trajectories.json @@ -0,0 +1 @@ +{"status":"done","method":"trajectories","why":"300 noisy trajectories of 2 qubits, shots that share their errors sharing a state","estimate":{"memory_bytes":268443904,"work":80},"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n","shots":300,"seed":5,"method":"trajectories","observables":["Z0 Z1"],"noise":{"depolarizing_2q":0.03,"readout":0.02}},"result":{"method":"trajectories","qubits":2,"gates":2,"accuracy":{"class":"estimate","trajectories":300,"histories":43,"gate_applications":64,"note":"each shot is an independent noisy trajectory in law; shots that took the same errors share one state (histories counts the distinct ones); expectations are means over the trajectories with their standard errors"},"expectations":[{"observable":"Z0 Z1","coeff":1.0,"value":0.9733333333333334,"stderr":0.013266275070829938}],"counts":{"11":142,"00":138,"01":12,"10":8},"distinct_outcomes":4,"omitted_shots":0,"shots":300,"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","energy":{"unmetered":"the command line does not meter a run's energy"},"receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"408d160222d612a9f6ecf44cf871c3cfd9b17c0c796d303149a6098411d716ba","signature":"b979ee8c0328be81843d57499b648f29a07441cd56d0e6b2071c08e31e043425cf758979c86c3a2505abedc03ffcf8c909dd998d3f54725b77035e92f5f6ac0c"}}} diff --git a/wai/quantum-conformance/receipts/jobs/cli-set-programs.json b/wai/quantum-conformance/receipts/jobs/cli-set-programs.json new file mode 100644 index 000000000..7bcec95a6 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/cli-set-programs.json @@ -0,0 +1 @@ +{"status":"done","method":"set","why":"a set of 2 items, run one after another in one process (exact, stabilizer)","estimate":{"memory_bytes":134218112,"work":176},"request":{"seed":3,"shots":64,"programs":["OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n","OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n"]},"result":{"format":"wai.quantum.set/1","count":2,"done":2,"failed":0,"root":"64593524fe46385009353a9c265d08b1ef5add6248bec0713f8f5dd0650714bd","energy":{"unmetered":"the command line does not meter a run's energy"},"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.set/1","receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"1569eb78f42db9a29a45bfe3a55d44d6888b665c9bdd150213655d23748bbcc7","signature":"d9fb355d4552ad7e5ed43025d4b9db64fd89a04562f6e330c85dad5143718ab472ca0d3ef7da39e7744c558710ff86631302f8c124958792434baa243d1f8202"},"items":[{"index":0,"request":{"seed":3,"shots":64,"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n"},"path":["dc8533cd8995ddd5d170447f7eddbf6fb54a90ef8ec8c0a32943c953e5c71adf"],"status":"done","method":"stabilizer","why":"Clifford circuit: the stabilizer tableau of 2 qubits","result":{"method":"stabilizer","qubits":2,"gates":2,"accuracy":{"class":"exact"},"counts":{"00":36,"11":28},"distinct_outcomes":2,"omitted_shots":0,"outcome_dimension":1,"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","provenance":{"schema":"wai.quantum.provenance/1","circuit":{"program":"bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a","qubits":2,"gates":2},"computer":{"kind":"simulator","method":"stabilizer","accuracy":"exact"},"compilation":{"asked":false,"status":"not-applicable","why":"the job asked for no compile: the program ran as the reader read it, on the method's own gates"},"execution":{"shots":64,"seed":3,"plan":{"method":"stabilizer","why":"Clifford circuit: the stabilizer tableau of 2 qubits","memory_bytes":67112966,"work":136}},"software":{"engine":"wai-quantum 0.8.1","host":"wai-quantum 0.8.1"}},"energy":{"unmetered":"an item of a set: the set's own figure covers every item together, since they run in one process"},"receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e","signature":"15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604"}}},{"index":1,"request":{"seed":3,"shots":64,"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n"},"path":["ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc"],"status":"done","method":"exact","why":"bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate","result":{"method":"exact","qubits":3,"gates":5,"accuracy":{"class":"bit-exact"},"state_hash":"5f1ae3e98c0555290986daf0d573de16a34ba01d1993f13f134e0b1daf6f1ab5","counts":{"000":34,"111":30},"distinct_outcomes":2,"omitted_shots":0,"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","provenance":{"schema":"wai.quantum.provenance/1","circuit":{"program":"ea71913eeaa729b80f38c332ee03b2a2bb08d3430f37617ad64c83f1f91b78a9","qubits":3,"gates":5},"computer":{"kind":"simulator","method":"exact","accuracy":"bit-exact"},"compilation":{"asked":false,"status":"not-applicable","why":"the job asked for no compile: the program ran as the reader read it, on the method's own gates"},"execution":{"shots":64,"seed":3,"plan":{"method":"exact","why":"bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate","memory_bytes":134218112,"work":40}},"software":{"engine":"wai-quantum 0.8.1","host":"wai-quantum 0.8.1"}},"energy":{"unmetered":"an item of a set: the set's own figure covers every item together, since they run in one process"},"receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"ddeb9ad8daad97246d28081c7a3f8d3152f975185a25261ec063eaba60f3e38b","signature":"c2c2c23f8d939e94069ef7501507c8763203324ef122247b8b748827539ccb129da636905f457ba3a62fab97153bcbc700190e9fa516c5a52115d631a3bb6600"}}}]}} diff --git a/wai/quantum-conformance/receipts/jobs/cli-set-sweep.json b/wai/quantum-conformance/receipts/jobs/cli-set-sweep.json new file mode 100644 index 000000000..1183684aa --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/cli-set-sweep.json @@ -0,0 +1 @@ +{"status":"done","method":"set","why":"a set of 3 items, run one after another in one process (statevector)","estimate":{"memory_bytes":268437025,"work":6},"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\ninput float theta;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n","shots":64,"seed":9,"sweep":{"theta":[0.25,0.5,1.5]}},"result":{"format":"wai.quantum.set/1","count":3,"done":3,"failed":0,"root":"9f35eeaa9a93eeb51f9fdd91cfb1da9c6e77bb4f02733cbf0ee63c3c94a3014a","energy":{"unmetered":"the command line does not meter a run's energy"},"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.set/1","receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"e1af3a39c0daa5551f08a9e9f37624566d6e02ab49b0527867d022c1472afa65","signature":"3a4985d53e8bf0e8f303a6009808e703078ad6f70845f1d551e3ef756ced45efc1f34ff97c9f6465d968f28d00f5830968177aee63cf08275613c2ba5c84b00e"},"items":[{"index":0,"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nconst float theta = 0.25;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n","shots":64,"seed":9},"path":["95043424c186bd5641172d73e3db14c529870ad8c4ad31c12b1f7c1641f52330","a3cb6daf6e4aeea406af8b15fed951ccea11461ff000d97878abe038e39f1fc9"],"point":{"theta":0.25},"status":"done","method":"statevector","why":"dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.25) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.25 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]","result":{"method":"statevector","qubits":1,"gates":1,"accuracy":{"class":"exact","fusion":3},"counts":{"0":62,"1":2},"distinct_outcomes":2,"omitted_shots":0,"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","provenance":{"schema":"wai.quantum.provenance/1","circuit":{"program":"c95e6d9dad59b9d07544b09a9b1b826b4786c9591e56fccf66c1373498d5b18e","qubits":1,"gates":1},"computer":{"kind":"simulator","method":"statevector","accuracy":"exact"},"compilation":{"asked":false,"status":"not-applicable","why":"the job asked for no compile: the program ran as the reader read it, on the method's own gates"},"execution":{"shots":64,"seed":9,"plan":{"method":"statevector","why":"dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.25) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.25 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]","memory_bytes":268437025,"work":2}},"software":{"engine":"wai-quantum 0.8.1","host":"wai-quantum 0.8.1"}},"energy":{"unmetered":"an item of a set: the set's own figure covers every item together, since they run in one process"},"receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"bdcc96b2490c1f61e2d70162637fd857aeb22bb508bda30a818addc47126374e","signature":"7621530b8fa28c96ce9f5ff0137694b52ef49937bc49f282df44cf17458415211dafbdc67de7a592b2a8a6a7a97888fdbc933aed3aa172c078bbe7afdfbb640c"}}},{"index":1,"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nconst float theta = 0.5;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n","shots":64,"seed":9},"path":["7d2993808de20a704e43f32f8050c5b5d4c4cab6d9ac9a5b1eb4639dc456c517","a3cb6daf6e4aeea406af8b15fed951ccea11461ff000d97878abe038e39f1fc9"],"point":{"theta":0.5},"status":"done","method":"statevector","why":"dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.5) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.5 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]","result":{"method":"statevector","qubits":1,"gates":1,"accuracy":{"class":"exact","fusion":3},"counts":{"0":60,"1":4},"distinct_outcomes":2,"omitted_shots":0,"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","provenance":{"schema":"wai.quantum.provenance/1","circuit":{"program":"29c4daa711120eaab818a9406a4697287d50c22222762fca04a8e5db0aa89a77","qubits":1,"gates":1},"computer":{"kind":"simulator","method":"statevector","accuracy":"exact"},"compilation":{"asked":false,"status":"not-applicable","why":"the job asked for no compile: the program ran as the reader read it, on the method's own gates"},"execution":{"shots":64,"seed":9,"plan":{"method":"statevector","why":"dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.5) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.5 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]","memory_bytes":268437025,"work":2}},"software":{"engine":"wai-quantum 0.8.1","host":"wai-quantum 0.8.1"}},"energy":{"unmetered":"an item of a set: the set's own figure covers every item together, since they run in one process"},"receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"7088942d5e36e3cc86977bbd096704e6cceac557730b6c8ce415720c093b554e","signature":"4499dda0bf56a8829a6a166fc0518f014ed9bf6c2545869583888c82060a134ae9f1f67604c30507c83c650719f86f612fe74ddacca29cb6ae654ea928375205"}}},{"index":2,"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nconst float theta = 1.5;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n","shots":64,"seed":9},"path":["10a868a06061e0d1f65a84fe4debe8878b4ef8898cea01b0661398b0064125dc","e82c037670d3dea4c2fdfbeaab601de68e80f138f5b3416c803835c74246de49"],"point":{"theta":1.5},"status":"done","method":"statevector","why":"dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(1.5) is not a Clifford gate; exact: exact needs dyadic angles: the angle 1.5 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]","result":{"method":"statevector","qubits":1,"gates":1,"accuracy":{"class":"exact","fusion":3},"counts":{"0":36,"1":28},"distinct_outcomes":2,"omitted_shots":0,"elapsed_ms":0,"engine":{"name":"wai-quantum","version":"0.8.1"},"schema":"wai.quantum.job-result/1","provenance":{"schema":"wai.quantum.provenance/1","circuit":{"program":"59827680d2db446f6ce85a185c00be9f351895dfe357d8d77aa619cb4f3ab49e","qubits":1,"gates":1},"computer":{"kind":"simulator","method":"statevector","accuracy":"exact"},"compilation":{"asked":false,"status":"not-applicable","why":"the job asked for no compile: the program ran as the reader read it, on the method's own gates"},"execution":{"shots":64,"seed":9,"plan":{"method":"statevector","why":"dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(1.5) is not a Clifford gate; exact: exact needs dyadic angles: the angle 1.5 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]","memory_bytes":268437025,"work":2}},"software":{"engine":"wai-quantum 0.8.1","host":"wai-quantum 0.8.1"}},"energy":{"unmetered":"an item of a set: the set's own figure covers every item together, since they run in one process"},"receipt":{"alg":"ed25519","canon":"rfc8785","key":"197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61","digest":"2c2c81405655d4ad81771ba70c71e2d29fe284d51a203d43b86977155aa10090","signature":"b9c3a4397dff4c27ef4825e66fe6be49f8bf66aec82b78d150da6705758d17687147bed8c1c2e28770c6e37d0fb7d7895922442109368449fa3d12fe9a28670f"}}}]}} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-canon-near-max.json b/wai/quantum-conformance/receipts/jobs/crafted-canon-near-max.json new file mode 100644 index 000000000..561803aa6 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-canon-near-max.json @@ -0,0 +1,19 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;" + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "x": 1.7976931348623158e308, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "38b08b73a410c7a1619be74a16edbf8db9ca08ceaef467ba5ca11e713b7bcb3a", + "signature": "8650700f67fb205794512aa57353d5055b5f6b2394d84307b793534f1180c0f1c392545117dbf459d90a7ef2b53e1854033b0b296c58046caafd8d88e3f1f709" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-canon-numbers.json b/wai/quantum-conformance/receipts/jobs/crafted-canon-numbers.json new file mode 100644 index 000000000..967dd971e --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-canon-numbers.json @@ -0,0 +1,35 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;" + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "x": [ + 1e+21, + 0.0000001, + 1e-06, + 0.30000000000000004, + 5e-324, + 1.7976931348623157e+308, + -0.0, + 1.2345678901234568e+20, + 9007199254740992, + 18446744073709551616, + 0, + -1, + 1e+300, + 4.35, + 1.234e-06 + ], + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "2a2e6e3d195d7a4c6b53542ade620b6255dc426799ad368b2a5986d154e978a0", + "signature": "ef8d8a6e82dede7cf48fe706043f50eb5ed5a5dbfba4312cdabf4ffdc3001b062985cee6b617926ba6d84bb4dc9cc11eee330d4961c98574fa0b0e5da1e81c00" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-canon-respelt.json b/wai/quantum-conformance/receipts/jobs/crafted-canon-respelt.json new file mode 100644 index 000000000..818021060 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-canon-respelt.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "observables": [ + "Z0 Z1", + "X0" + ], + "seed": 7, + "shots": 1.0e3, + "qasm": "\u004fPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "result": { + "method": "stabilizer", + "\u0071ubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-canon-strings.json b/wai/quantum-conformance/receipts/jobs/crafted-canon-strings.json new file mode 100644 index 000000000..bac677131 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-canon-strings.json @@ -0,0 +1,21 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;", + "😀": "a key above the BMP", + "": "sorts after it in UTF-16", + "z": "tab\tnewline\ncontrol\u0001 slash/ del sep
 nonchar￿ quote\" backslash\\" + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "738887d81b8fd18027905bcf38d0457c117ae763e8006a74214279423290defc", + "signature": "b496932e763c4b6749caf0fdf5648eaf42777fddef97e17a92745d225d2c0bc22da4dfde963dfe71a4c263ec0c5f17f7680a2f9a9c287bff674470a3f8f62b08" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-canon-underflow.json b/wai/quantum-conformance/receipts/jobs/crafted-canon-underflow.json new file mode 100644 index 000000000..46b4c3016 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-canon-underflow.json @@ -0,0 +1,19 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;" + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "x": 1e-400, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "97577c4ef701bf5167d2d4596eca50b6d82d7cfdff09765b0c385667d4337aa2", + "signature": "fb0bb7f4be78632c21eccc7014b21cc57a60556a8c803f9d719d669bf640831dffa9915092a1069e461904c83aea30b28e39a1ae60e719701a0220dec6573b00" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-earlier-approximate.json b/wai/quantum-conformance/receipts/jobs/crafted-earlier-approximate.json new file mode 100644 index 000000000..95e06bc78 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-earlier-approximate.json @@ -0,0 +1,66 @@ +{ + "status": "done", + "method": "provider", + "why": "a fixture", + "request": { + "qasm": "OPENQASM 3.0; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;", + "provider": "lab", + "shots": 64, + "seed": 1 + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts measured on the device the provider names, as it returned them, with no mitigation; not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them", + "approximate": true + }, + "shots": 64, + "provider": { + "name": "lab", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference", + "provenance": "simulator" + }, + "calibration": "snap-1", + "compiled": { + "basis": "rzsxxcz", + "program": "00" + }, + "job": { + "id": "j-1", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03125, + "hellinger_fidelity": 0.9990224819584785, + "sampling_tvd_expected": 0.04986778505017909, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the measured counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's error, or a program the device did not run as sent" + }, + "cost": { + "free": true + }, + "energy": { + "unmetered": "the work ran at the provider" + }, + "counts": { + "11": 34, + "00": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "573a683a1abb7741b602272be809171baa68c9e5518f74c38400d5c79cdd70a3", + "signature": "489b2b3ff4ad78425c27fe6723e6fdf22113d590ab880093739cab5494f23244ff99bd7dfd8ac067d1d7e5aa785d338bc6d8654bed794fa748519371caa8d80c" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-earlier-request-shots.json b/wai/quantum-conformance/receipts/jobs/crafted-earlier-request-shots.json new file mode 100644 index 000000000..500aa8385 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-earlier-request-shots.json @@ -0,0 +1,65 @@ +{ + "status": "done", + "method": "provider", + "why": "a fixture", + "request": { + "qasm": "OPENQASM 3.0; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;", + "provider": "lab", + "shots": 65, + "seed": 1 + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts measured on the device the provider names, as it returned them, with no mitigation; not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them" + }, + "shots": 64, + "provider": { + "name": "lab", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference", + "provenance": "simulator" + }, + "calibration": "snap-1", + "compiled": { + "basis": "rzsxxcz", + "program": "00" + }, + "job": { + "id": "j-1", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03125, + "hellinger_fidelity": 0.9990224819584785, + "sampling_tvd_expected": 0.04986778505017909, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the measured counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's error, or a program the device did not run as sent" + }, + "cost": { + "free": true + }, + "energy": { + "unmetered": "the work ran at the provider" + }, + "counts": { + "11": 34, + "00": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "a085a8a9f881ae3261af34c7587958db608dbcdf108c8a44e74de59828ab4189", + "signature": "50627e0eb5cd6d6c1c7249c213c9199c78b2e96e63a85aed7db39738cc13e256d3d89e68fe7bbafd764912da0612bcc7c66ec1beecea671936ad0c5c7da6ba02" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-earlier-simulated.json b/wai/quantum-conformance/receipts/jobs/crafted-earlier-simulated.json new file mode 100644 index 000000000..7f6caf0b5 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-earlier-simulated.json @@ -0,0 +1,65 @@ +{ + "status": "done", + "method": "provider", + "why": "a fixture", + "request": { + "qasm": "OPENQASM 3.0; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;", + "provider": "lab", + "shots": 64, + "seed": 1 + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts measured on the device the provider names, as it returned them, with no mitigation; not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them" + }, + "shots": 64, + "provider": { + "name": "lab", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference", + "provenance": "simulator" + }, + "calibration": "snap-1", + "compiled": { + "basis": "rzsxxcz", + "program": "00" + }, + "job": { + "id": "j-1", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03125, + "hellinger_fidelity": 0.9990224819584785, + "sampling_tvd_expected": 0.04986778505017909, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the measured counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's error, or a program the device did not run as sent" + }, + "cost": { + "free": true + }, + "energy": { + "unmetered": "the work ran at the provider" + }, + "counts": { + "11": 34, + "00": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "0a9c0322d070ee97e6841db873be08d490955262fcc5afa5fb7d16091fb6b320", + "signature": "32643e6ace3b7fd30980d8d4af64069681eadf980b97821501827bd9e3aa3b39eab7e18a6ba4128d234940426c5cf4f16a839ef1f9623d86797295904a476d0f" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-envelope-changed.json b/wai/quantum-conformance/receipts/jobs/crafted-envelope-changed.json new file mode 100644 index 000000000..2747c409f --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-envelope-changed.json @@ -0,0 +1,62 @@ +{ + "status": "failed", + "method": "a different method", + "why": "anything at all", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + }, + "id": "j-another" +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-form-canonical-relabelled.json b/wai/quantum-conformance/receipts/jobs/crafted-form-canonical-relabelled.json new file mode 100644 index 000000000..6f52ea664 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-form-canonical-relabelled.json @@ -0,0 +1,60 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-form-first-big-integer.json b/wai/quantum-conformance/receipts/jobs/crafted-form-first-big-integer.json new file mode 100644 index 000000000..2c2bcbbc1 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-form-first-big-integer.json @@ -0,0 +1,60 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 9007199254740993, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "6b5de0b8636bf8e9fa5af54242e1f4242ba26b87fa4d7994f7f56f3cd0c48cf7", + "signature": "20934c32a4e9997bfa20ad9ac8d83ac285ea7f343976f0381a64220cbb4603d1a7c0ad06eced8ea3ff05e7100bb3f08103dbadf3364a7f0228c06d41dd445803" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-form-first-exponents.json b/wai/quantum-conformance/receipts/jobs/crafted-form-first-exponents.json new file mode 100644 index 000000000..1178961cf --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-form-first-exponents.json @@ -0,0 +1,68 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "x": [ + 1e+16, + 1.5e+300, + 1e-07, + 2.5e-06, + 1000000000000000.0, + 1e-05 + ], + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "12bea8e71977cbfebf01757f160fd70e568cc41c8d9caa6e3691d5bc4e7835ae", + "signature": "5b20510bfdfe9236c4bfafbc7e28045e60fecc3e19f96eb016f1f7bdddd7a1aca248d37ad15e665d43709be56b663b65d755371d341da4460b2d21296200c307" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-form-first-past-its-integers-exact.json b/wai/quantum-conformance/receipts/jobs/crafted-form-first-past-its-integers-exact.json new file mode 100644 index 000000000..1e82b7f7d --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-form-first-past-its-integers-exact.json @@ -0,0 +1,60 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 18446744073709551616, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "c6509f912d15031a7c42fd57ec8e3ad6de8d72ff739ef0eef24cb281caf76aa7", + "signature": "bf2c455a07932c56c38b233e904339045e4710e3925fe99483654bf7af098825e7acc6448acdbf85e998150c7324f0e5146a33b69c9bcd7f0fdaf80a0cd7510c" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-form-first-past-its-integers.json b/wai/quantum-conformance/receipts/jobs/crafted-form-first-past-its-integers.json new file mode 100644 index 000000000..a980d2fee --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-form-first-past-its-integers.json @@ -0,0 +1,60 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 18446744073709551617, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "c6509f912d15031a7c42fd57ec8e3ad6de8d72ff739ef0eef24cb281caf76aa7", + "signature": "bf2c455a07932c56c38b233e904339045e4710e3925fe99483654bf7af098825e7acc6448acdbf85e998150c7324f0e5146a33b69c9bcd7f0fdaf80a0cd7510c" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-form-first-relabelled.json b/wai/quantum-conformance/receipts/jobs/crafted-form-first-relabelled.json new file mode 100644 index 000000000..652dd3e85 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-form-first-relabelled.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "410af825d4570eaf11bb082bfaeef5b7088eff7354f883f1babd528f431360ac", + "signature": "5999bc51c289421b22dd1c50861767638674e924cd749a09ff24cf01dcadfc956f6d882fd98af0607a813415ed8ec4a6fd55f0fcc30a0565a76351a9de553f03", + "canon": "rfc8785" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-form-first-reordered.json b/wai/quantum-conformance/receipts/jobs/crafted-form-first-reordered.json new file mode 100644 index 000000000..ba93bce8b --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-form-first-reordered.json @@ -0,0 +1,60 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "schema": "wai.quantum.job-result/1", + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "elapsed_ms": 0, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "outcome_dimension": 1, + "omitted_shots": 0, + "distinct_outcomes": 2, + "counts": { + "11": 505, + "00": 495 + }, + "accuracy": { + "class": "exact" + }, + "gates": 2, + "qubits": 2, + "method": "stabilizer", + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "410af825d4570eaf11bb082bfaeef5b7088eff7354f883f1babd528f431360ac", + "signature": "5999bc51c289421b22dd1c50861767638674e924cd749a09ff24cf01dcadfc956f6d882fd98af0607a813415ed8ec4a6fd55f0fcc30a0565a76351a9de553f03" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-form-first-respelt.json b/wai/quantum-conformance/receipts/jobs/crafted-form-first-respelt.json new file mode 100644 index 000000000..c76cfb6b3 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-form-first-respelt.json @@ -0,0 +1,60 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "410af825d4570eaf11bb082bfaeef5b7088eff7354f883f1babd528f431360ac", + "signature": "5999bc51c289421b22dd1c50861767638674e924cd749a09ff24cf01dcadfc956f6d882fd98af0607a813415ed8ec4a6fd55f0fcc30a0565a76351a9de553f03" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-form-first.json b/wai/quantum-conformance/receipts/jobs/crafted-form-first.json new file mode 100644 index 000000000..478b11234 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-form-first.json @@ -0,0 +1,60 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "410af825d4570eaf11bb082bfaeef5b7088eff7354f883f1babd528f431360ac", + "signature": "5999bc51c289421b22dd1c50861767638674e924cd749a09ff24cf01dcadfc956f6d882fd98af0607a813415ed8ec4a6fd55f0fcc30a0565a76351a9de553f03" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-form-none-inexact.json b/wai/quantum-conformance/receipts/jobs/crafted-form-none-inexact.json new file mode 100644 index 000000000..407acecac --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-form-none-inexact.json @@ -0,0 +1,60 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 9007199254740993, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "rsa-pss", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "6b5de0b8636bf8e9fa5af54242e1f4242ba26b87fa4d7994f7f56f3cd0c48cf7", + "signature": "20934c32a4e9997bfa20ad9ac8d83ac285ea7f343976f0381a64220cbb4603d1a7c0ad06eced8ea3ff05e7100bb3f08103dbadf3364a7f0228c06d41dd445803" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-job-array.json b/wai/quantum-conformance/receipts/jobs/crafted-job-array.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-job-array.json @@ -0,0 +1 @@ +[] diff --git a/wai/quantum-conformance/receipts/jobs/crafted-job-no-receipt.json b/wai/quantum-conformance/receipts/jobs/crafted-job-no-receipt.json new file mode 100644 index 000000000..b510acdcd --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-job-no-receipt.json @@ -0,0 +1,54 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-job-no-request.json b/wai/quantum-conformance/receipts/jobs/crafted-job-no-request.json new file mode 100644 index 000000000..825e5799c --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-job-no-request.json @@ -0,0 +1,52 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-job-request-string.json b/wai/quantum-conformance/receipts/jobs/crafted-job-request-string.json new file mode 100644 index 000000000..9a024c528 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-job-request-string.json @@ -0,0 +1,53 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": "OPENQASM 3.0;", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-no-names.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-no-names.json new file mode 100644 index 000000000..5c1774c69 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-no-names.json @@ -0,0 +1,112 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, prx-circuits:test-qc (prx-circuits): compiled exactly to prxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "prx-circuits", + "protocol": "prx-circuits", + "device": "prx-circuits:test-qc", + "provenance": "silicon", + "qubits": 5, + "basis": "prxcz", + "describe": "1f6d996884468f37481db4e20932d6c0ecb84ebbbf4232f734080c61ab9fae66" + }, + "calibration": null, + "compiled": { + "basis": "prxcz", + "program": "12123632bbbc70df6b58c3103f50479a72211ba109a01d9e3a581c703f160dad", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ngate prx(theta, phi) a { rz(-phi) a; rx(theta) a; rz(phi) a; }\nbit[2] c;\nprx(pi/2, -pi/2) $0;\nprx(pi/2, -pi/2) $1;\ncz $0, $1;\nprx(pi/2, pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 4, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "prx-0", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "vendor_counts", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "circuits", + "shots" + ], + "settings": {} + }, + "verbatim": false, + "note": "this client sends only circuits and shots: it sets nothing, so every step is the service's default, whatever that is", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "86bf68db554f8d287455b0c625ee84590a3e74a8d16ff4faf03381593f09d5cc", + "signature": "d5d07ff3c01fefdee339298305a904cddc45b0d7b90f6ab8d3c00af43ff32349100737e49eb3467c450a0e84db743a6e0a9ecaab904f270768be4035f0fa3001" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-nobody.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-nobody.json new file mode 100644 index 000000000..3d51f2339 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-nobody.json @@ -0,0 +1,104 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, prx-circuits:test-qc (prx-circuits): compiled exactly to prxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "prx-circuits", + "protocol": "prx-circuits", + "device": "prx-circuits:test-qc", + "provenance": "silicon", + "qubits": 5, + "basis": "prxcz", + "describe": "1f6d996884468f37481db4e20932d6c0ecb84ebbbf4232f734080c61ab9fae66" + }, + "calibration": null, + "compiled": { + "basis": "prxcz", + "program": "12123632bbbc70df6b58c3103f50479a72211ba109a01d9e3a581c703f160dad", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ngate prx(theta, phi) a { rz(-phi) a; rx(theta) a; rz(phi) a; }\nbit[2] c;\nprx(pi/2, -pi/2) $0;\nprx(pi/2, -pi/2) $1;\ncz $0, $1;\nprx(pi/2, pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 4, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "prx-0", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "vendor_counts", + "execution": { + "mitigation": "unknown", + "twirling": "unknown", + "decoupling": "unknown", + "remapping": "unknown", + "optimisation": "unknown", + "stated_by": "nobody", + "verbatim": null + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ab960231ba7179c48cc1409e73cae3aea506638c566cbfc2dfc671e40b75e2f3", + "signature": "ec8629fc3f60fc2f1eae13e424c18bb0a004f45a6bf1bec7fe7fea69c619be0672e59112714d91e66b3e99c418cb7b3c676d976e66225e795a7a08b3583c2c0f" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-shots.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-shots.json new file mode 100644 index 000000000..93b0c1f93 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-shots.json @@ -0,0 +1,116 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, prx-circuits:test-qc (prx-circuits): compiled exactly to prxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "prx-circuits", + "protocol": "prx-circuits", + "device": "prx-circuits:test-qc", + "provenance": "silicon", + "qubits": 5, + "basis": "prxcz", + "describe": "1f6d996884468f37481db4e20932d6c0ecb84ebbbf4232f734080c61ab9fae66" + }, + "calibration": null, + "compiled": { + "basis": "prxcz", + "program": "12123632bbbc70df6b58c3103f50479a72211ba109a01d9e3a581c703f160dad", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ngate prx(theta, phi) a { rz(-phi) a; rx(theta) a; rz(phi) a; }\nbit[2] c;\nprx(pi/2, -pi/2) $0;\nprx(pi/2, -pi/2) $1;\ncz $0, $1;\nprx(pi/2, pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 4, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "QB1", + "1": "QB2" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "prx-0", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "circuits", + "shots" + ], + "settings": {} + }, + "verbatim": false, + "note": "this client sends only circuits and shots: it sets nothing, so every step is the service's default, whatever that is", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "128b98f68d728554428f1a732bb26656b8559e535ae45e90e054f4c09d5f65af", + "signature": "508b0388baf6595f949af0eb741f22efe99adb6b7da9787bcba685898f72b5b46484a73db9263cf6257a430ee87ad0967598276d8e1eb44341241241a18f5e00" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-twirling-off.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-twirling-off.json new file mode 100644 index 000000000..dc4bb3365 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-prx-twirling-off.json @@ -0,0 +1,116 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, prx-circuits:test-qc (prx-circuits): compiled exactly to prxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "prx-circuits", + "protocol": "prx-circuits", + "device": "prx-circuits:test-qc", + "provenance": "silicon", + "qubits": 5, + "basis": "prxcz", + "describe": "1f6d996884468f37481db4e20932d6c0ecb84ebbbf4232f734080c61ab9fae66" + }, + "calibration": null, + "compiled": { + "basis": "prxcz", + "program": "12123632bbbc70df6b58c3103f50479a72211ba109a01d9e3a581c703f160dad", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ngate prx(theta, phi) a { rz(-phi) a; rx(theta) a; rz(phi) a; }\nbit[2] c;\nprx(pi/2, -pi/2) $0;\nprx(pi/2, -pi/2) $1;\ncz $0, $1;\nprx(pi/2, pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 4, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "QB1", + "1": "QB2" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "prx-0", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "vendor_counts", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "circuits", + "shots" + ], + "settings": {} + }, + "verbatim": false, + "note": "this client sends only circuits and shots: it sets nothing, so every step is the service's default, whatever that is", + "mitigation": "default", + "twirling": "off", + "decoupling": "default", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "a41b3702444401d46fd34d13306ba891ceb1b29aad4581243fa435f9b1f389ef", + "signature": "1e11d6731be36a5299a7c22b5632bb7741b8bb1c39546c476175c3886601c6cc273b974889e1c5875d020d38c151e19e25bd4bce631fc577292ae84229844a01" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-sampler-names-short.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-sampler-names-short.json new file mode 100644 index 000000000..36ec9cbd2 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-sampler-names-short.json @@ -0,0 +1,118 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, sampler-v2:test_device (sampler-v2): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (its shot records, the provider's own counts, or counts rebuilt here from its probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "sampler-v2", + "protocol": "sampler-v2", + "device": "sampler-v2:test_device", + "provenance": "silicon", + "qubits": 6, + "basis": "rzsxxcz", + "describe": "f25d38ec41b6c4e16dc2d5d2649b2ca23cddacb67d3de1b06add9766b5e3b879" + }, + "calibration": null, + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + }, + "qubit_names": {} + }, + "job": { + "id": "job-0", + "polls": 2 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "backend", + "params", + "program_id" + ], + "settings": { + "/params/options/dynamical_decoupling/enable": false, + "/params/options/twirling/enable_gates": false, + "/params/options/twirling/enable_measure": false + } + }, + "verbatim": false, + "note": "dynamical decoupling and twirling of gates and measurements are sent off; the request sets nothing for mitigation, remapping or optimisation, so the service's defaults stand; the program is sent on physical qubits in the backend's own gates", + "mitigation": "default", + "twirling": "off", + "decoupling": "off", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "45beb4425cf0d92d77e1cfddadbdb56dfefc254e2d1eba96f8b49520d0e00cc2", + "signature": "589803c0a54dc10b636ac7c4c420ad6e2da884e1d14cb18a191335556386a8504cdbe442482982be783e188ee6e9bec7925eeda95448a9cacf7a36f8354be10e" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-sampler-twirling-default.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-sampler-twirling-default.json new file mode 100644 index 000000000..bd83bb7ff --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-sampler-twirling-default.json @@ -0,0 +1,117 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, sampler-v2:test_device (sampler-v2): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (its shot records, the provider's own counts, or counts rebuilt here from its probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "sampler-v2", + "protocol": "sampler-v2", + "device": "sampler-v2:test_device", + "provenance": "silicon", + "qubits": 6, + "basis": "rzsxxcz", + "describe": "f25d38ec41b6c4e16dc2d5d2649b2ca23cddacb67d3de1b06add9766b5e3b879" + }, + "calibration": null, + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "job-0", + "polls": 2 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "backend", + "params", + "program_id" + ], + "settings": { + "/params/options/dynamical_decoupling/enable": false, + "/params/options/twirling/enable_gates": false, + "/params/options/twirling/enable_measure": false + } + }, + "verbatim": false, + "note": "dynamical decoupling and twirling of gates and measurements are sent off; the request sets nothing for mitigation, remapping or optimisation, so the service's defaults stand; the program is sent on physical qubits in the backend's own gates", + "mitigation": "default", + "twirling": "default", + "decoupling": "off", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ee7cda3e0086a441525afe201c4e41683dfb8ba396a0abbdc9661b496717be8a", + "signature": "7e8ef63bb51d7e10590c8423e3115d08673c4c337e1170d01f1669f8883a93d97b5df4d8227444d9bdf5e86fab91d70043d7321c1f34fab4f74d025fe00d680d" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-name-leading-zero.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-name-leading-zero.json new file mode 100644 index 000000000..d9667baee --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-name-leading-zero.json @@ -0,0 +1,127 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, cloud-tasks:Line (cloud-tasks): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "cloud-tasks", + "protocol": "cloud-tasks", + "device": "cloud-tasks:Line", + "provenance": "silicon", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "4f18897e3839ecb995e7baa9eb493c654ef257b47f3056b3925be06cf1517964" + }, + "calibration": "2026-11-05T00:00:00Z", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "4", + "1": "7", + "01": "x" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "arn:task:0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": false, + "amount": "0.470000", + "currency": "USD", + "unit": "shot", + "note": "the device's price per shot, which its API states, and the service's fee per task of 0.30 USD, from its price list of 2026-11-05, which its API does not state" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "action", + "clientToken", + "deviceArn", + "outputS3Bucket", + "outputS3KeyPrefix", + "shots" + ], + "settings": { + "verbatim_box": true + } + }, + "verbatim": true, + "note": "the processor's gates go in the service's verbatim box on its physical qubits, so the service neither remaps nor optimises them; the task sets no device parameters, so mitigation, twirling and decoupling are the service's defaults", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "off", + "optimisation": "off" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "8a016562d8b1df385fccd9820223718a4122e51bdecd86eadec1011d7fa26748", + "signature": "b516fde68f000eecf4e4f0a34bb5ae54924f8f7b1891c9a8aa9348873c491d623f6f2ab526f84614fa1432840987b0c7148b86ccb8004166a308412e8fb94f06" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-names-null.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-names-null.json new file mode 100644 index 000000000..db13bd95b --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-names-null.json @@ -0,0 +1,123 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, cloud-tasks:Line (cloud-tasks): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "cloud-tasks", + "protocol": "cloud-tasks", + "device": "cloud-tasks:Line", + "provenance": "silicon", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "4f18897e3839ecb995e7baa9eb493c654ef257b47f3056b3925be06cf1517964" + }, + "calibration": "2026-11-05T00:00:00Z", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": null, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "arn:task:0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": false, + "amount": "0.470000", + "currency": "USD", + "unit": "shot", + "note": "the device's price per shot, which its API states, and the service's fee per task of 0.30 USD, from its price list of 2026-11-05, which its API does not state" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "action", + "clientToken", + "deviceArn", + "outputS3Bucket", + "outputS3KeyPrefix", + "shots" + ], + "settings": { + "verbatim_box": true + } + }, + "verbatim": true, + "note": "the processor's gates go in the service's verbatim box on its physical qubits, so the service neither remaps nor optimises them; the task sets no device parameters, so mitigation, twirling and decoupling are the service's defaults", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "off", + "optimisation": "off" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ff200a0bd771b0bc1c8497d893907bcf90085a18ff99c6c4a442565963cf019b", + "signature": "7957621c5af0f293f88aabed6712d7883c67f06a4776067e7a736ad314dcbe84441332b1acbf87f96581f9232bc67dc4da043af04a10b3b6f6a599e2ff2ccf0b" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-names-short-of-text.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-names-short-of-text.json new file mode 100644 index 000000000..327b4ddd7 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-names-short-of-text.json @@ -0,0 +1,126 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, cloud-tasks:Line (cloud-tasks): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "cloud-tasks", + "protocol": "cloud-tasks", + "device": "cloud-tasks:Line", + "provenance": "silicon", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "4f18897e3839ecb995e7baa9eb493c654ef257b47f3056b3925be06cf1517964" + }, + "calibration": "2026-11-05T00:00:00Z", + "compiled": { + "basis": "rzsxxcz", + "program": "5bae13a719b91ebff41d268eebb83fd1b16d1048b1a25c1a55ef6e88501bad35", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\nx $2;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "4", + "1": "7" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "arn:task:0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": false, + "amount": "0.470000", + "currency": "USD", + "unit": "shot", + "note": "the device's price per shot, which its API states, and the service's fee per task of 0.30 USD, from its price list of 2026-11-05, which its API does not state" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "action", + "clientToken", + "deviceArn", + "outputS3Bucket", + "outputS3KeyPrefix", + "shots" + ], + "settings": { + "verbatim_box": true + } + }, + "verbatim": true, + "note": "the processor's gates go in the service's verbatim box on its physical qubits, so the service neither remaps nor optimises them; the task sets no device parameters, so mitigation, twirling and decoupling are the service's defaults", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "off", + "optimisation": "off" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "784cccccedf14b1e8d405563a9641ec224d067ebec1c3b10b0598c75d288de52", + "signature": "4c9d215e18729eac140cff7c7580f54e61d44343b40487b3d9059a53dcd23fdd35ac04a434da149c7a2f283708ac12476e31cb56e2148afcb43a53f999898500" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-no-names.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-no-names.json new file mode 100644 index 000000000..3059e2400 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-no-names.json @@ -0,0 +1,122 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, cloud-tasks:Line (cloud-tasks): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "cloud-tasks", + "protocol": "cloud-tasks", + "device": "cloud-tasks:Line", + "provenance": "silicon", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "4f18897e3839ecb995e7baa9eb493c654ef257b47f3056b3925be06cf1517964" + }, + "calibration": "2026-11-05T00:00:00Z", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "arn:task:0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": false, + "amount": "0.470000", + "currency": "USD", + "unit": "shot", + "note": "the device's price per shot, which its API states, and the service's fee per task of 0.30 USD, from its price list of 2026-11-05, which its API does not state" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "action", + "clientToken", + "deviceArn", + "outputS3Bucket", + "outputS3KeyPrefix", + "shots" + ], + "settings": { + "verbatim_box": true + } + }, + "verbatim": true, + "note": "the processor's gates go in the service's verbatim box on its physical qubits, so the service neither remaps nor optimises them; the task sets no device parameters, so mitigation, twirling and decoupling are the service's defaults", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "off", + "optimisation": "off" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "8e2e3610308bc46034a4a07be586f3221902b104604b8992bc12fa2a700dae96", + "signature": "d7b4cde297a98abb049d51351e9b372c4cb9045829a5fe5d3f862357331fc66ab00d22ce02c66fe7bee3d793084dbe27cfa30b2f9ffdc75bd8fb9fccc1811004" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-processor-unboxed.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-processor-unboxed.json new file mode 100644 index 000000000..ef3423df3 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-processor-unboxed.json @@ -0,0 +1,126 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, cloud-tasks:Line (cloud-tasks): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "cloud-tasks", + "protocol": "cloud-tasks", + "device": "cloud-tasks:Line", + "provenance": "silicon", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "4f18897e3839ecb995e7baa9eb493c654ef257b47f3056b3925be06cf1517964" + }, + "calibration": "2026-11-05T00:00:00Z", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "4", + "1": "7" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "arn:task:0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": false, + "amount": "0.470000", + "currency": "USD", + "unit": "shot", + "note": "the device's price per shot, which its API states, and the service's fee per task of 0.30 USD, from its price list of 2026-11-05, which its API does not state" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "action", + "clientToken", + "deviceArn", + "outputS3Bucket", + "outputS3KeyPrefix", + "shots" + ], + "settings": { + "verbatim_box": true + } + }, + "verbatim": false, + "note": "the processor's gates go in the service's verbatim box on its physical qubits, so the service neither remaps nor optimises them; the task sets no device parameters, so mitigation, twirling and decoupling are the service's defaults", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "off", + "optimisation": "off" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "6b3d84a5949f1c2f311ab7939b28ced42e82b5100d2f97d20c7f2e27087625c4", + "signature": "2600b145497eba0b4f2f7f483c891f619f75df4ff661a11ca6af6ea0c3491d14345d54363ec14a9ac58a5eed72b44dd3b24ce991b8f2f537d125e8392502c80a" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-simulator-boxed.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-simulator-boxed.json new file mode 100644 index 000000000..f4e0668c9 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-simulator-boxed.json @@ -0,0 +1,127 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, cloud-tasks:SIM (cloud-tasks): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "cloud-tasks", + "protocol": "cloud-tasks", + "device": "cloud-tasks:SIM", + "provenance": "simulator:the service's SIM simulator", + "qubits": 34, + "basis": "rzsxxcz", + "describe": "d052d88256742b435f059d894f7ef4287325f0516a445bd0d689a4e2c4157deb" + }, + "calibration": "2026-11-05T00:00:00Z", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "0", + "1": "1" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "arn:task:0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": false, + "amount": "0.003750", + "currency": "USD", + "unit": "minute", + "note": "the device's price for its three-second minimum, at least; the time a task takes is not known before it runs", + "at_least": true + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "action", + "clientToken", + "deviceArn", + "outputS3Bucket", + "outputS3KeyPrefix", + "shots" + ], + "settings": { + "verbatim_box": true + } + }, + "verbatim": true, + "note": "the processor's gates go in the service's verbatim box on its physical qubits, so the service neither remaps nor optimises them; the task sets no device parameters, so mitigation, twirling and decoupling are the service's defaults", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "off", + "optimisation": "off" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "93c46b86f6a3f184c8952bcb856fcc7e5cff9c22d9e8a67ecd5035914e3efdd9", + "signature": "1bc1688bad4b491825ba275c12857bcea607daedc1a9218bc97139eff311d5c7dc84d6f32d464a27bf62b0e1d39c3454fbe1cad6c458beb0dd647e8c213d7a0f" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-vendor-counts.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-vendor-counts.json new file mode 100644 index 000000000..13a05e79d --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-tasks-vendor-counts.json @@ -0,0 +1,126 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, cloud-tasks:Line (cloud-tasks): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "cloud-tasks", + "protocol": "cloud-tasks", + "device": "cloud-tasks:Line", + "provenance": "silicon", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "4f18897e3839ecb995e7baa9eb493c654ef257b47f3056b3925be06cf1517964" + }, + "calibration": "2026-11-05T00:00:00Z", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "4", + "1": "7" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "arn:task:0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": false, + "amount": "0.470000", + "currency": "USD", + "unit": "shot", + "note": "the device's price per shot, which its API states, and the service's fee per task of 0.30 USD, from its price list of 2026-11-05, which its API does not state" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "vendor_counts", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "action", + "clientToken", + "deviceArn", + "outputS3Bucket", + "outputS3KeyPrefix", + "shots" + ], + "settings": { + "verbatim_box": true + } + }, + "verbatim": true, + "note": "the processor's gates go in the service's verbatim box on its physical qubits, so the service neither remaps nor optimises them; the task sets no device parameters, so mitigation, twirling and decoupling are the service's defaults", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "off", + "optimisation": "off" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "9883d3dd925c68b101a72180e2159ab69f59fce1c2c22900b1c0af9e6c9a9660", + "signature": "e2b8c69980fd755bb0e15673bf3807da765ae605a75ead11ba7669b8e1b4b487e9cdd5395c34266a33b911e70a3dec353a9f03c949185fa87e7f2e32ec9e940b" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-kind-turns-shots.json b/wai/quantum-conformance/receipts/jobs/crafted-kind-turns-shots.json new file mode 100644 index 000000000..12478b9b2 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-kind-turns-shots.json @@ -0,0 +1,120 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, native-turns:simulator (native-turns): compiled exactly to gpizz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "native-turns", + "protocol": "native-turns", + "device": "native-turns:simulator", + "provenance": "simulator:native-turns simulator, noise ideal", + "qubits": 8, + "basis": "gpizz", + "describe": "93a83ea1002d3b86cfd40a37a5b7db3b3e4f4e39ef2bfac05e248495e2c24278" + }, + "calibration": null, + "compiled": { + "basis": "gpizz", + "program": "7bfa03b310e68ffe2cf1357652f9e6dadd812c852855b3ba0f2864943bbea9ff", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ngate gpi(phi) a { rz(-2*pi*phi) a; x a; rz(2*pi*phi) a; }\ngate gpi2(phi) a { rz(-2*pi*phi) a; rx(pi/2) a; rz(2*pi*phi) a; }\ngate zz(theta) a, b { cx a, b; rz(2*pi*theta) b; cx a, b; }\nbit[2] c;\ngpi2(0.75) $0;\ngpi2(0.75) $1;\nzz(0.25) $0, $1;\ngpi2(0.5) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 4, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "turns-0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait", + "service_kwh": 0.0, + "service_kwh_is": "the provider's own figure, not a measurement here" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "backend", + "input", + "settings", + "shots", + "type" + ], + "settings": { + "/input/gateset": "native", + "/settings/error_mitigation/debiasing": false + } + }, + "verbatim": false, + "note": "debiasing is sent off and the program goes in the service's native gate set; the request sets nothing for twirling, decoupling, remapping or optimisation, so the service's defaults stand", + "mitigation": "off", + "twirling": "default", + "decoupling": "default", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d09075254a3f802a1ad8faabf962b9907cfad9dfdc9a21d70b23a490ee24f22f", + "signature": "7a4991206d0dbbe67a2140f7b477fc0bbf09d67f60b946ebc737a2c12840a07a8515a7901ce6cb73a7ad089422e5a54f223ba53144d4dce7da4335732fc5c005" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-method-class-unknown.json b/wai/quantum-conformance/receipts/jobs/crafted-method-class-unknown.json new file mode 100644 index 000000000..831420a20 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-method-class-unknown.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "quasi-exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "3d5a689240f1cacd169bddfd7db35ce77af605dcfbe65777e233946470eacf97", + "signature": "480b1181eb0e3acdbbd20fef80fbecf68deb37921900c04d76ec0ef91ac0040df00ad2ac47517eb88a8df8266dcf11005886e79e0b8a50c36e51cd320d107f01" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-method-measured.json b/wai/quantum-conformance/receipts/jobs/crafted-method-measured.json new file mode 100644 index 000000000..48feb741b --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-method-measured.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "42a25dcb02ba434508276e951bff188ddcdf89d55683f3fd508d9d65b714d3e1", + "signature": "14997c952486fe7b3916d03fbd8a9fc8b5e8d47803984ce76280eb05b204ae803c333801818ea44263569c9548c470a5ed37ffbbcfc0a91d11cfd5299cf70406" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-method-simulated.json b/wai/quantum-conformance/receipts/jobs/crafted-method-simulated.json new file mode 100644 index 000000000..1cb408f7a --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-method-simulated.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ae019cf1502a8829b7fd693f5a5a61afc0e4fa7929388d3079351d8b8fa1cdf4", + "signature": "1bca0b0ee08e2ed1d8cd4c217d7bae76c1f6d1b11a7262517146164658f8df01d84fd0303404a15c2518f5c1a57349bff5e8603b83682189b44a76e306f4210e" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-number-canonical-spelling.json b/wai/quantum-conformance/receipts/jobs/crafted-number-canonical-spelling.json new file mode 100644 index 000000000..e89425e38 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-number-canonical-spelling.json @@ -0,0 +1,25 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;", + "observables": [ + { + "pauli": "Z0", + "coeff": 1152921504606847000 + } + ] + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "x": 1152921504606847000, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "eb498c98ce62608edcab5016e3af0c94c70d65b3beecbae8ba2e84a505254243", + "signature": "41c70f03324932ca36662a4a2901109774d73cb362c76130f17e217cdacb242fec2922140d66847bc0c384c200ea58501a01966e5f89f6ce8ac94072f886a907" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-number-exact-spelling.json b/wai/quantum-conformance/receipts/jobs/crafted-number-exact-spelling.json new file mode 100644 index 000000000..6dfc2bf5f --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-number-exact-spelling.json @@ -0,0 +1,25 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;", + "observables": [ + { + "pauli": "Z0", + "coeff": 1152921504606846976 + } + ] + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "x": 1152921504606846976, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "eb498c98ce62608edcab5016e3af0c94c70d65b3beecbae8ba2e84a505254243", + "signature": "41c70f03324932ca36662a4a2901109774d73cb362c76130f17e217cdacb242fec2922140d66847bc0c384c200ea58501a01966e5f89f6ce8ac94072f886a907" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-number-exponent-spelling.json b/wai/quantum-conformance/receipts/jobs/crafted-number-exponent-spelling.json new file mode 100644 index 000000000..042a00709 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-number-exponent-spelling.json @@ -0,0 +1,25 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;", + "observables": [ + { + "pauli": "Z0", + "coeff": 1.152921504606847e+18 + } + ] + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "x": 1.152921504606847e+18, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "eb498c98ce62608edcab5016e3af0c94c70d65b3beecbae8ba2e84a505254243", + "signature": "41c70f03324932ca36662a4a2901109774d73cb362c76130f17e217cdacb242fec2922140d66847bc0c384c200ea58501a01966e5f89f6ce8ac94072f886a907" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-number-inexact-past-u64.json b/wai/quantum-conformance/receipts/jobs/crafted-number-inexact-past-u64.json new file mode 100644 index 000000000..f0bba4ca2 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-number-inexact-past-u64.json @@ -0,0 +1,19 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;", + "seed": 18446744073709551617 + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "3c06347b7aa7d697eaa206e237179ee68b67bbceba8e196fbefbb99c3c122c37", + "signature": "528dcb256af682e5ebf4c734b850b93c6151d4fa5b4ba25f1ea22abb6697d29e106c13c7fe5b0afcc7a182669b46bda06831a8a6e739dd0e49ecd01927e43401" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-number-inexact-unsealed.json b/wai/quantum-conformance/receipts/jobs/crafted-number-inexact-unsealed.json new file mode 100644 index 000000000..98e690c5c --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-number-inexact-unsealed.json @@ -0,0 +1,62 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306", + "x": 9007199254740993 + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-number-inexact.json b/wai/quantum-conformance/receipts/jobs/crafted-number-inexact.json new file mode 100644 index 000000000..0be87e887 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-number-inexact.json @@ -0,0 +1,19 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;" + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "x": 9007199254740993, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "e8a66f8c172e999ece19cf286b198df2038b2e6e3ce2d5c204061296f9fcde83", + "signature": "053080cb8ca0ead84fbb3e75c6e069b416f74b7523786411b5ed0f52a33d1c0483a0f07cb894901130022e910f1f592e5c2fc5c20666e3a89cbeafc4c8785502" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-number-near-canonical.json b/wai/quantum-conformance/receipts/jobs/crafted-number-near-canonical.json new file mode 100644 index 000000000..30be8c8aa --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-number-near-canonical.json @@ -0,0 +1,25 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;", + "observables": [ + { + "pauli": "Z0", + "coeff": 1152921504606847001 + } + ] + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "x": 1152921504606847001, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "eb498c98ce62608edcab5016e3af0c94c70d65b3beecbae8ba2e84a505254243", + "signature": "41c70f03324932ca36662a4a2901109774d73cb362c76130f17e217cdacb242fec2922140d66847bc0c384c200ea58501a01966e5f89f6ce8ac94072f886a907" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-number-padded-past.json b/wai/quantum-conformance/receipts/jobs/crafted-number-padded-past.json new file mode 100644 index 000000000..108cf2ff6 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-number-padded-past.json @@ -0,0 +1,25 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;", + "observables": [ + { + "pauli": "Z0", + "coeff": 1152921504606847000000 + } + ] + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "x": 1152921504606847000000, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "fae4eeb2b5f749bfbd0088d5058e3cb989a507797525f0b17e482f97a67d71f6", + "signature": "467aa73214601634746aa31d69b5599255afc8ccf146fe9e611a85efb5f2797618dfa8d7dd7b84cc56171326c902440d2c575bb36c8e2814c534df9d62fcac03" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-approximate-false.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-approximate-false.json new file mode 100644 index 000000000..f382d7a21 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-approximate-false.json @@ -0,0 +1,111 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would", + "approximate": false + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "53a24c80734c29ba8e177d22544d9ca1493f49dd19703e3d9a5062040253afbb", + "signature": "390965477851b9be6e624aef2018e97407238f2e7652a9a067e934c0f3988380834973f9a6d3a68faeac8e5d694bdef1b3d73ae6548f2ca46c4d158f7765dc0a" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-approximate-processor.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-approximate-processor.json new file mode 100644 index 000000000..0f2580fff --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-approximate-processor.json @@ -0,0 +1,111 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would", + "approximate": true + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "silicon", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "a0170c7dadc4781f41f9c525243c6c53473a7c7ef94a88fc4269fad9fa6664ac", + "signature": "b5444fd3d2e702cd8634773c085fa5f4bad1e40298d2064c723bd0717eedf2be2c2f7c2b7485eea6d9d7ad902e86f764cc8836c9d6e9163337f4beb06ab90108" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-approximate-simulator.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-approximate-simulator.json new file mode 100644 index 000000000..2d9c66c2a --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-approximate-simulator.json @@ -0,0 +1,111 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would", + "approximate": true + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ffcfa88daa4929744766d35392b3bf7fdbcf51e97878cdb19fdbeae1a626172a", + "signature": "f0f689b80558d1d821c9813c71d0828a397f38b7108bbc42064551a22dad8bacd77b75addbc96c9a16453e1625e37766e7a0c278de912df3b079cb5dfdfa4101" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-compiled-program.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-compiled-program.json new file mode 100644 index 000000000..d98bcbf5c --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-compiled-program.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n// another program\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "dbbdd8d7770713a255219dacaf17bfa9bedbabd9fddd2bfd35d2fb33740e6055", + "signature": "6f7b9b1ac5cca2ed75c9358a36e9d6f411d2ce504900fd92535f53bfa0608e5e95197eeac065a438ca24c6d9317970e396634ac103a6d982fb718c7930d8a206" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-cost-free-confirmed.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-cost-free-confirmed.json new file mode 100644 index 000000000..27cbea5ee --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-cost-free-confirmed.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "416894d65a09f434ab45e7e1d309df13687edb32f22a699ab8beb905af55336e", + "signature": "15e91db43a12da7eb208084b23f1002957c424beb9e29e414920acc35c78554c1d40e7404e627a7dc16a91ce0b462995bf8652c7f2d985d73ff4fd10d1b1ea0f" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-cost-unconfirmed.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-cost-unconfirmed.json new file mode 100644 index 000000000..738ec9d26 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-cost-unconfirmed.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "abb478f903b50347fbace67ac93b52a749145473ff7608d7da610b706d98b556", + "signature": "14371789a598adf18bad0d815b0370a42c3c4943cdcb8cfb11e4333c56db1da474412f7339fc6a24da14aaf584b79c187c3762494848ea248fb75e9690bcae09" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-count-past-safe.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-count-past-safe.json new file mode 100644 index 000000000..06a70b2b8 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-count-past-safe.json @@ -0,0 +1,109 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 9007199254740992, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 9007199254740992, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "00": 9007199254740992 + }, + "distinct_outcomes": 1, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "1f3a3a5b7949ced04e4e00758cffd058f7969b5e61ea43928224b1a360f56b95", + "signature": "b62e1fa9a993348855e06ff555e325da1f6df3e743a293347e1c48166f7ae383ab1706e2c763d9af915ccb8dd65e471ba620f02e8388318996bc0fbd5af6aa00" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-distinct.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-distinct.json new file mode 100644 index 000000000..d49ff0816 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-distinct.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 3, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "b4e11e0fda32a94594634c874107cdea57a7db0b920317238eed4e1ed21610b7", + "signature": "eab21b089bb99f715ed2dd8beeb2f857bd8bb9f26a04258f009e2023670c1a94471e987b9a542aabc936d0444d016fea76ec9685454fc4c0ae5d8ac549513206" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-hidden.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-hidden.json new file mode 100644 index 000000000..b30ffa863 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-hidden.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 1, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 160, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "6ddeab08025ea957926fb546a2990a96a3f0de50d1e2e4132f26cdf20cbec1f7", + "signature": "341faa8cac441b2027ec22e9d1fee325789d82d5adc86a22855a265b488c2498ff1b43b50be0243706a7e55360d75e48cf9f916118f11fc494c2e1cff1e46e04" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-source-other.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-source-other.json new file mode 100644 index 000000000..e31de000c --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-source-other.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "estimated", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "e68d2efb118571f878bb785f009995bdfdbfee27cf9145ddb923a87697372d52", + "signature": "77545691059a152b805cd4db00032c6716219f8e370b9983e0029e29b632e2c951b0e6fcab36f8a26f0e1f172c794b83a206d5ef54488fab17925580dc85cb06" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-sum.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-sum.json new file mode 100644 index 000000000..25346bf02 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-sum.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 162, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "8d4168defab76623a89b778ee4d676558c5885b2720d6fa72414d5f55327b0f5", + "signature": "cfec72fdb01f209a6d6ff905129cddfe8021fe354d9ce9a963e42d3650511f65ac9ba5802d3200d1a4d4be90ba6fdfcf67939867b87e9fb1a3da33ce9c490b09" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-width.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-width.json new file mode 100644 index 000000000..3b317869d --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-width.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "00": 139, + "011": 161 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "9d84105009ef5704e1dfbe19ddbdd78528f2ea0371aed06b10e2a54c73deec8d", + "signature": "aea6cf228fff6dace4986ecb73e1b693795613c9c0ddb7a39f5fec5902f8c6e668ca0d1499a7e4ba97f09d35b1a256f0338dac4f554003107c788657b6a84202" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-zero.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-zero.json new file mode 100644 index 000000000..3a70adf41 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-counts-zero.json @@ -0,0 +1,111 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139, + "01": 0 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "6e0f7612ef1b19d005069efc02b9b83807bb1cb6cda4f42a3e6e4d365696db03", + "signature": "76554df3f58f3fefa8af77272f24fc43c0f06197c666932079f0d2cb5c1becd07f33eea2d56b2b8fe325259b2ef324171f739c42be7da3943ff5fd2b9cdbf407" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-client-open.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-client-open.json new file mode 100644 index 000000000..a6e0773d9 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-client-open.json @@ -0,0 +1,118 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "op", + "program", + "protocol", + "shots" + ], + "settings": {} + }, + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "default", + "optimisation": "default", + "verbatim": false + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "831db22daa6075dab614cce2c256408ff8f0271d489a95aa3e452cc27431980c", + "signature": "d9efaf8f21a457763fb3cbc4d00c0f1b4612a5959af8b2345f39432e6f8836658aca06bbe57327578952788e7a0368fd8a4589062a594fb611d483a67c158a0b" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-default.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-default.json new file mode 100644 index 000000000..e9b06dab5 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-default.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "default", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "5197193ff6270400bb6f4461f6ec593b96e390137331316bd20a2efc38a56ee8", + "signature": "b5806d30ca2e50d38d0a69a0a154d79b66b53b2d8fbb5914f28d47f9629ae36b4aa7c411fb70651cba28fcac6b38e1852e7a330d32e1725ae892bef61548460a" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-member.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-member.json new file mode 100644 index 000000000..7be756f3f --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-member.json @@ -0,0 +1,111 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing", + "routing": "off" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "128bc0e89aec43cca7eca18f568949dfa3e92831fe69b1e4aa6811c37eca618a", + "signature": "79c5b7b5b6a6227c03b8da2de19ecb79959cfc05e6a75606b69f88a331c0eba340d29c447d7aaad1416ba2683e4826f9ef8689e94f764951ccb1e5ed9a4a2e06" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-nobody.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-nobody.json new file mode 100644 index 000000000..fad2ad9d9 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-nobody.json @@ -0,0 +1,109 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "nobody", + "mitigation": "unknown", + "twirling": "unknown", + "decoupling": "unknown", + "remapping": "unknown", + "optimisation": "unknown", + "verbatim": null + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "63867279cdd62766e2f33c28b88c0375002ff9e3b8ca02ae6637eaca076077c4", + "signature": "5e680a5eb6b4d30fe5f475a5fd0e4226edbe17c6951c7b4e11ac5e55660c01774c8af75baeb13f488e5c1bfa73fe4cd4f4c942c8128a3bd04e68ca4752b5b502" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-sent.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-sent.json new file mode 100644 index 000000000..88588d289 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-sent.json @@ -0,0 +1,114 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing", + "sent": { + "fields": [], + "settings": {} + } + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "2ef0bb61fcc0696f9e6b9af9f0da91d19cdcabdaa353493b5789d786603749b4", + "signature": "92c66c6e65bafd6a7280a47e429953d73106591557bc62dd26959689d852ad54dedf4e811fa204bdd700380a7a551e95162be75535dfe46bc209654719fbfe0a" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-verbatim-remapped.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-verbatim-remapped.json new file mode 100644 index 000000000..849a48754 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-execution-verbatim-remapped.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "on", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "8e35f338722b0821d5a5a0713d355506293a536f7ae37414c5c4c5d365979f32", + "signature": "f96207f0db94318aeede85cfd6b4e672e945032bb57a7693045b590ef86fb9807d4f0eeaf788e16c1bcafc0248e372862caac9f68a0bd1ae3f44fa52ee07050e" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-measured-simulator.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-measured-simulator.json new file mode 100644 index 000000000..14570044f --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-measured-simulator.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "6524c9ea6f565cbe2ff62f26ded9dfaf34b7648e0d545cce552c0b865c3aa686", + "signature": "6957ee0c7c8943dfb15a8842e7905eb6108eeab1faf742b3fa90908db7715c3f3c3598791d92ea91376725db736333313693f93ed7617370e5fd02f704a1ff04" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-members-basis.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-basis.json new file mode 100644 index 000000000..078029967 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-basis.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "gpizz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "288a58d62f269e89c6f8173402f529f436a9a2ccd6720ba9676d0bb18b64fdbd", + "signature": "31bd8f0d729389a35093f4df742c3a2ac1f121d7d21cb6a6f25a0cab855bf0defa7c2d5fc944a371f80d88ab3378249397c49e526b1e190f34fda7d5e13cea09" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-members-describe.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-describe.json new file mode 100644 index 000000000..1ba60f039 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-describe.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0A802ECD608FB5BCA73C5A22FF65B5A08A408416501BB5472986F6540FF91AC0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "52d70a3b6fe93eb44ae03882a764960bcc1eb2fdf7d4f422b9858e69aa852a2e", + "signature": "9830c7b6da35f20d3d4102e3ec4d0af9f7fb5de5805cdfdaa759071894d51bdaa38e58cc746253852dd8947c3d8bec99b4ea29aacb790ba8dca118d166dcca0e" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-members-energy.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-energy.json new file mode 100644 index 000000000..07e300df6 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-energy.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "joules": 1 + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "80e568e4d5790e83948f1f5f0f71bbfa990883b808ab667546ffcafd980dd422", + "signature": "e1b621c0f312d7e4715b54cfc26e6a7c3fc209b2278fcfc7fd36b9ffb09f6f75182421b7fc6e870e888114077877372067bd1e2cbd22bd4c863e3e49c2efc706" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-members-kwh-unlabelled.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-kwh-unlabelled.json new file mode 100644 index 000000000..ed43afff2 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-kwh-unlabelled.json @@ -0,0 +1,111 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait", + "service_kwh": 0.001 + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "cc72f69cda6ae55ce068d645ca844961b7ea385aab25335b9f0cbb4596a6c505", + "signature": "36ab084f44e689a63684c9b6ace0d72d4e78c4a1c50a56e2e199fdb7fe65047668f83ee49233e58d5c3876c0caba4bc13670917f2f1237f76984f2a7d1885207" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-members-no-charged.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-no-charged.json new file mode 100644 index 000000000..35f760ede --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-no-charged.json @@ -0,0 +1,109 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "e1eefde445b25fc90b5f868d7e7dc1eae29e66e5c75d2a52fd3eadb81dbaf89a", + "signature": "f4c83e613e7ef3ec05168e12d00a238c000631b9d89feb684dbb909684714ee073d9d989f0ec2cb6de525a609e5e55498d92253590dccfe704786c770c53d101" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-members-no-qasm3.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-no-qasm3.json new file mode 100644 index 000000000..ad49c109b --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-no-qasm3.json @@ -0,0 +1,109 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "0a6a07f5611128567616488705b97eacecb4f2d8a406e3dad0906ddac8c84d0f", + "signature": "af5175fcde8335f080b71fe658cbdca248062f1a20f5dcab4aa7a99c26fa5553398a45e2530e830500eed002e974b42a425c92e939348044c32e194116dd5d0f" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-members-no-schema.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-no-schema.json new file mode 100644 index 000000000..682af789f --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-members-no-schema.json @@ -0,0 +1,109 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "df75644499c9ba9df7d63cd5a82adb372fba8db0cb490f38beb5dd70ea19c383", + "signature": "0498f9fd724c41b186009df51b4b3bde9123d5c99ca14107941e628176ab20629679f149dcb7e4f81c784a8a98777d02ea4fa6a72c63bcf04d11338999493c05" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-provenance-unknown.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-provenance-unknown.json new file mode 100644 index 000000000..721c96235 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-provenance-unknown.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "fpga", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "c4b76d3b703f9df40165f058a261a0c0c7e3adf756a5b4c464b726ffceedbc9a", + "signature": "c53953f6b5f7dc81a5983a7e77bffa0caa752b00ec97bcb297ba9117b7a677f82a6a50d479be8d1200efec444e30af37b32266c77e267ebe1104b64ddf26760f" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-request-provider.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-request-provider.json new file mode 100644 index 000000000..71b5df2e3 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-request-provider.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "another" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "baec14babd10e8617958a2f944b8a8d112560425b77e023100a8d13e82126ace", + "signature": "953797764f25231f6891475c272702a9499efd51d0a6336fc02e33ef9f784f2a125b20ae68bb14866cef06cabbdc915c55cf49090fb8daac16acf06536732d0a" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-request-shots-default.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-request-shots-default.json new file mode 100644 index 000000000..109825df6 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-request-shots-default.json @@ -0,0 +1,109 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "fa0c7f28ee31cb7e437675c6ef4c0ed1a3c6ffc1309d1fde595cc3e6109630ca", + "signature": "8e71819f0de1edb60963b27b91c82507ecdcfaa984c07e4fb29b714d426874db8398ca68f92259bb69a61564a9f4c2991e8ab7bea0460fc7d10867b174c6a10d" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-request-shots.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-request-shots.json new file mode 100644 index 000000000..da051a094 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-request-shots.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 301, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "e528861f1237ea1bab1d39bdf101eb568158ddc5d1b923d47627a7cf130fab92", + "signature": "7be5a852fef8a52ffe51b3bc0a96b1856197ed7a2f63a30e19bde14ed6754415425fe27df806c760e4f5828f522b87cc619bda1e0aad87fb058a5964d27dae03" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-shots-past-safe.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-shots-past-safe.json new file mode 100644 index 000000000..6248fe048 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-shots-past-safe.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 9007199254740992, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 9007199254740992, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "00": 9007199254740991, + "11": 1 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "297b98016ca44fc33575a138cf78079c98a23c1aa0cee2cc26ead6d587847650", + "signature": "1cace6a2c4be4d7abe90227f8ae813a974148e19955fd27627c764000b31b46e022449d861746929e237e57d600094a47deea21c8dcdd24f5b6088b171016f09" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-shots-same-strings.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-shots-same-strings.json new file mode 100644 index 000000000..e64854f6d --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-shots-same-strings.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": "300", + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": "300", + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "18fcd6aac46e7dfe36a4b62a946fc0a3c2aa4fce3803a6d66a32699b79941030", + "signature": "1782029378b1ac0740610d1afa49e2deae519c217d66037c57ed103f79380dbb1f8b521038e339cb9af1a4e3b0826507ece222fe4e45f4f1f0b36595e5f67c01" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-stated-by-a-null.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-stated-by-a-null.json new file mode 100644 index 000000000..15ea5898c --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-stated-by-a-null.json @@ -0,0 +1,66 @@ +{ + "status": "done", + "method": "provider", + "why": "a fixture", + "request": { + "qasm": "OPENQASM 3.0; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;", + "provider": "lab", + "shots": 64, + "seed": 1 + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts measured on the device the provider names, as it returned them, with no mitigation; not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them" + }, + "shots": 64, + "provider": { + "name": "lab", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference", + "provenance": "simulator" + }, + "calibration": "snap-1", + "compiled": { + "basis": "rzsxxcz", + "program": "00" + }, + "job": { + "id": "j-1", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03125, + "hellinger_fidelity": 0.9990224819584785, + "sampling_tvd_expected": 0.04986778505017909, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the measured counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's error, or a program the device did not run as sent" + }, + "cost": { + "free": true + }, + "energy": { + "unmetered": "the work ran at the provider" + }, + "counts": { + "11": 34, + "00": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "counts_source": null, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "a8d5e44dc4fd121763093df683da27857aa533419f7029c46f9b30c2f959ee64", + "signature": "6c1d0296c5ae3efbc2f224eb5830fe98ab82994f43c387d75423df324a6a153c7c1bd14f616da0a61e9ce6efc549c701ed442b9b35437bc243e1fffe5181e708" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-whole-past-safe.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-whole-past-safe.json new file mode 100644 index 000000000..e989924ae --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-whole-past-safe.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 9007199254740992 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "dffaac0234a6e5e27cdff3bc8d04c70727c442798f2339d21e1b18f7e12c03db", + "signature": "f6acb4b67d4a941666a4d4719990786dc629160b9f178a428bc0d5c0067eb97211113819244114865e1f4c3811e94a70642716ebd19c986b21b1e36717465009" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-provider-whole-spelt-large.json b/wai/quantum-conformance/receipts/jobs/crafted-provider-whole-spelt-large.json new file mode 100644 index 000000000..5bfd7685f --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-provider-whole-spelt-large.json @@ -0,0 +1,110 @@ +{ + "status": "done", + "method": "provider", + "why": "on ref, reference:line-5 (wai.quantum.provider/1): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 300, + "seed": 4, + "provider": "ref" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 300, + "provider": { + "name": "ref", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference:line-5", + "provenance": "simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given", + "qubits": 1e+30, + "basis": "rzsxxcz", + "describe": "0a802ecd608fb5bca73c5a22ff65b5a08a408416501bb5472986f6540ff91ac0" + }, + "calibration": "reference-f2d9fe570421", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description states no errors" + } + }, + "job": { + "id": "ref-1", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03666666666666665, + "hellinger_fidelity": 0.998653743148044, + "sampling_tvd_expected": 0.023032943298089033, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": true, + "estimate": { + "free": true, + "seconds": 0.0 + }, + "charged": null, + "confirmed": false + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "provider", + "mitigation": "none", + "twirling": "none", + "decoupling": "none", + "remapping": "none", + "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + "counts": { + "11": 161, + "00": 139 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "53d624333bb8a70f69e1ec1862e3fa96dc95e01b334bf6c907f6d21a36d64796", + "signature": "e6fcd40c20b22c0085a0982039547fda305c42fea3f986e0e33604a09b09d924dbe9343de6c992ee0dfaff10bc675fb8366109ff979d68d76b15b96b32a07806" + } + }, + "id": "pj-442c6043319912f8", + "submitted_ms": 1793907034817, + "finished_ms": 1793907034817 +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-byte-order-mark.json b/wai/quantum-conformance/receipts/jobs/crafted-read-byte-order-mark.json new file mode 100644 index 000000000..008d0ea36 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-byte-order-mark.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-depth-127.json b/wai/quantum-conformance/receipts/jobs/crafted-read-depth-127.json new file mode 100644 index 000000000..b5209e14e --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-depth-127.json @@ -0,0 +1,267 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;", + "nest": [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "5b70aa76c6758e62e24e9bf807c483ed0ed7760afd671fc2e52c982919032a19", + "signature": "cef9b9da0cea433f41e71eff6f7fe6c981fdece8d4f80c511a295b12607156784ff3ba1d45a9113eff74ace1c6837859428aea64a2db0497575dbab1d1120103" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-depth-128.json b/wai/quantum-conformance/receipts/jobs/crafted-read-depth-128.json new file mode 100644 index 000000000..4948b625b --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-depth-128.json @@ -0,0 +1,269 @@ +{ + "request": { + "qasm": "OPENQASM 3.0;", + "nest": [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + }, + "result": { + "method": "statevector", + "accuracy": { + "class": "exact" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "78af6eef8a7b5e4d3ef91b19ad7211bff89abe23f76441b40ede9d37edbc664b", + "signature": "000b165e1b685f5e265de81ecf89f460a6071e6baba5325d7054c387f0a09de7f11c314408c441580c0d4a6d93bb2c0fe6e5a2183103a2979797c418869ed00a" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-duplicate-escaped.json b/wai/quantum-conformance/receipts/jobs/crafted-read-duplicate-escaped.json new file mode 100644 index 000000000..d1b0e20f9 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-duplicate-escaped.json @@ -0,0 +1,62 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubit\u0073": 7, + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-duplicate-unsealed.json b/wai/quantum-conformance/receipts/jobs/crafted-read-duplicate-unsealed.json new file mode 100644 index 000000000..9597459b4 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-duplicate-unsealed.json @@ -0,0 +1,62 @@ +{ + "status": "failed", + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-duplicate.json b/wai/quantum-conformance/receipts/jobs/crafted-read-duplicate.json new file mode 100644 index 000000000..6027e6d63 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-duplicate.json @@ -0,0 +1,62 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 7, + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-lone-low-surrogate.json b/wai/quantum-conformance/receipts/jobs/crafted-read-lone-low-surrogate.json new file mode 100644 index 000000000..695222e30 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-lone-low-surrogate.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "\udc00OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-lone-surrogate.json b/wai/quantum-conformance/receipts/jobs/crafted-read-lone-surrogate.json new file mode 100644 index 000000000..30982aa88 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-lone-surrogate.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "\ud800OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-not-json.json b/wai/quantum-conformance/receipts/jobs/crafted-read-not-json.json new file mode 100644 index 000000000..d4aea27dd --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-not-json.json @@ -0,0 +1 @@ +signature VALID diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-not-utf8.json b/wai/quantum-conformance/receipts/jobs/crafted-read-not-utf8.json new file mode 100644 index 000000000..23fa5de5c --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-not-utf8.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "ÿPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-number-at-limit.json b/wai/quantum-conformance/receipts/jobs/crafted-read-number-at-limit.json new file mode 100644 index 000000000..8486499b8 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-number-at-limit.json @@ -0,0 +1,62 @@ +{ + "status": "done", + "x": 179769313486231580793728971405303415079934132710037826936173778980444968292764750946649017977587207096330286416692887910946555547851940402630657488671505820681908902000708383676273854845817711531764475730270069855571366959622842914819860834936475292719074168444365510704342711559699508093042880177904174497792, + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-number-overflow.json b/wai/quantum-conformance/receipts/jobs/crafted-read-number-overflow.json new file mode 100644 index 000000000..51f2c9add --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-number-overflow.json @@ -0,0 +1,62 @@ +{ + "status": "done", + "x": 1e400, + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-raw-control.json b/wai/quantum-conformance/receipts/jobs/crafted-read-raw-control.json new file mode 100644 index 000000000..51ebc95bd --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-raw-control.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": " OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-read-trailing.json b/wai/quantum-conformance/receipts/jobs/crafted-read-trailing.json new file mode 100644 index 000000000..6ec15c289 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-read-trailing.json @@ -0,0 +1,62 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} +{} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-alg-other.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-alg-other.json new file mode 100644 index 000000000..33106ccec --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-alg-other.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "rsa-pss", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-canon-null.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-canon-null.json new file mode 100644 index 000000000..6af380311 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-canon-null.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": null, + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-canon-other.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-canon-other.json new file mode 100644 index 000000000..07084e2df --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-canon-other.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "jcs", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-digest-other.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-digest-other.json new file mode 100644 index 000000000..cb5f447b7 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-digest-other.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "f91e18e02e6f36948a65898242d16ca8fce3e32482c032ac019f3c20b7d98db8", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-digest-plus.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-digest-plus.json new file mode 100644 index 000000000..5ebb243fb --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-digest-plus.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da34+5192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-digest-stale.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-digest-stale.json new file mode 100644 index 000000000..1b9b5f13b --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-digest-stale.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 0.5 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-key-missing.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-missing.json new file mode 100644 index 000000000..d8ff71778 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-missing.json @@ -0,0 +1,60 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-key-mixed-order.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-mixed-order.json new file mode 100644 index 000000000..d9276c5b3 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-mixed-order.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "5e2a648325bc9a8f481c735248eda873674ff86f0a74835f61c4b8e2eb0a4879", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-key-noncanonical.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-noncanonical.json new file mode 100644 index 000000000..4244881e1 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-noncanonical.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-key-off-curve.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-off-curve.json new file mode 100644 index 000000000..8b17b9c66 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-off-curve.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "0200000000000000000000000000000000000000000000000000000000000000", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-key-two-byte-char.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-two-byte-char.json new file mode 100644 index 000000000..5c9d9ed9c --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-two-byte-char.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "aéf6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-key-upper.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-upper.json new file mode 100644 index 000000000..4d888ffb9 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-key-upper.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197F6B23E16C8532C6ABC838FACD5EA789BE0C76B2920334039BFA8B3D368D61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a62306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-r-noncanonical.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-r-noncanonical.json new file mode 100644 index 000000000..43bb11b23 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-r-noncanonical.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fc91c68d9de45f2eb028f83d125d712e1f9ef88abe2cec3647b6d1d5a6df3b504" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-r-small-order.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-r-small-order.json new file mode 100644 index 000000000..74932308a --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-r-small-order.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "01000000000000000000000000000000000000000000000000000000000000007b36a73c06b96d210e32271fd68e9f6521c5f28166dc185aac2e51872f9ad209" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-receipt-string.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-receipt-string.json new file mode 100644 index 000000000..58efaf74e --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-receipt-string.json @@ -0,0 +1,55 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": "VALID" + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-s-plus-l.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-s-plus-l.json new file mode 100644 index 000000000..a313da6c2 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-s-plus-l.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e3c9e386c3c90609222f76f2b901bbd3d3abbd59aabc56d3a9d9164b7d8a62316" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-signature-other-key.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-signature-other-key.json new file mode 100644 index 000000000..9e38a0e75 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-signature-other-key.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "4d9f76acee79cf49735bde262adadc3c30a0baeb6578cfb9908987e164ba5e8a355c60a7cbfcb8dd04b80f67ef44de6be055043fe01c8a700146a41d06bcfe0c" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-signature-short.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-signature-short.json new file mode 100644 index 000000000..3c048644a --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-signature-short.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "d4eaa3cf71da3405192198a7297a653644d773dc3e94761cda44b84eff45ffd1", + "signature": "7cd39fd1eacf313bfd17db9cce2d93aba8ff8ca1900de2a67a5b2770ea705e7e4fca420f222d4e3a4c5a7888b121de283abbd59aabc56d3a9d9164b7d8a623" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-weak-key-any-digest.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-weak-key-any-digest.json new file mode 100644 index 000000000..1705e909a --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-weak-key-any-digest.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": -1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "digest": "4a7c154163cafc6c747993ecd79597aae2cdae310d784bcfeb95b284270a237d", + "signature": "58666666666666666666666666666666666666666666666666666666666666660100000000000000000000000000000000000000000000000000000000000000" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-seal-weak-key-forgery.json b/wai/quantum-conformance/receipts/jobs/crafted-seal-weak-key-forgery.json new file mode 100644 index 000000000..f98f6f65a --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-seal-weak-key-forgery.json @@ -0,0 +1,61 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67172870, + "work": 2016 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 1000, + "seed": 7, + "observables": [ + "Z0 Z1", + "X0" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "11": 505, + "00": 495 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": -1.0 + }, + { + "observable": "X0", + "coeff": 1.0, + "value": 0.0 + } + ], + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "0100000000000000000000000000000000000000000000000000000000000000", + "digest": "4a7c154163cafc6c747993ecd79597aae2cdae310d784bcfeb95b284270a237d", + "signature": "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-count-other.json b/wai/quantum-conformance/receipts/jobs/crafted-set-count-other.json new file mode 100644 index 000000000..05484ef2c --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-count-other.json @@ -0,0 +1,196 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 3, + "done": 2, + "failed": 0, + "root": "64593524fe46385009353a9c265d08b1ef5add6248bec0713f8f5dd0650714bd", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "41acdb8e42bf98e9cea0c94e3856b497232c9467896e24ba07953947488c9ee7", + "signature": "3b4a68d41e08405975415ea9670c719db91578bbb43b18999b5fe2b031399b753fa870dfcc9389e06828d61a49d281e8e9160729c596c5a4e9feb279f0e9a406" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "dc8533cd8995ddd5d170447f7eddbf6fb54a90ef8ec8c0a32943c953e5c71adf" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 1, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "result": { + "method": "exact", + "qubits": 3, + "gates": 5, + "accuracy": { + "class": "bit-exact" + }, + "state_hash": "5f1ae3e98c0555290986daf0d573de16a34ba01d1993f13f134e0b1daf6f1ab5", + "counts": { + "000": 34, + "111": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "ea71913eeaa729b80f38c332ee03b2a2bb08d3430f37617ad64c83f1f91b78a9", + "qubits": 3, + "gates": 5 + }, + "computer": { + "kind": "simulator", + "method": "exact", + "accuracy": "bit-exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "memory_bytes": 134218112, + "work": 40 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ddeb9ad8daad97246d28081c7a3f8d3152f975185a25261ec063eaba60f3e38b", + "signature": "c2c2c23f8d939e94069ef7501507c8763203324ef122247b8b748827539ccb129da636905f457ba3a62fab97153bcbc700190e9fa516c5a52115d631a3bb6600" + } + } + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-empty.json b/wai/quantum-conformance/receipts/jobs/crafted-set-empty.json new file mode 100644 index 000000000..631a691e3 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-empty.json @@ -0,0 +1,41 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 0, + "done": 2, + "failed": 0, + "root": "0000000000000000000000000000000000000000000000000000000000000000", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "329130c94b3aaf8d8a51d45963ffdc2b5d4ce0aa95fd6b630fdbe818fd991d3a", + "signature": "e0cd1a7cfcade6518f0c20e51aeebf4952fcbad3390ef83ab81607478c5859afc832397efd24180de12e711bceeb433945655af8f64b63f84d70f707646b0e07" + }, + "items": [] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-error-item-inexact.json b/wai/quantum-conformance/receipts/jobs/crafted-set-error-item-inexact.json new file mode 100644 index 000000000..672fd53b4 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-error-item-inexact.json @@ -0,0 +1,134 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "53e029e74970410ccf9440d3b3fda3f77a19f89c5b058650f7d9d1d526d01b05", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "b527172437979020f52c3c5a0f2bdecc6540df086efde4cdb936307f00504e43", + "signature": "3db830c3f631e3be0d69d9094b87f837b5f50342ca4f4248aea37459428ab98957b135e6743c9f6f373aa6740f23a9db66a2399b184de8f242dbc9112145260c" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "82a0a8dba2287a9bbd2ce7d705511ed166fa37b1765b0c7a1fbc3f419caafb94" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 1, + "request": { + "seed": 9007199254740993, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "error": "it did not run" + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-error-item.json b/wai/quantum-conformance/receipts/jobs/crafted-set-error-item.json new file mode 100644 index 000000000..99cf154ed --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-error-item.json @@ -0,0 +1,134 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "8535543a064b51c65cbd03e7f638d8639eacd05d2c9d3da9beff017d391edc93", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "f862b33f98701b2d23f4c39d1ffd25471f0622e255669757245acf348825033b", + "signature": "8051412530b2cd6785a13d936d552fb5b1f454d6e0418ee53551bca2ce84d523c2385afe4671ff08ac0eeced209869de4a4a314b3ec0e6da4a764817a30a030e" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "50c93c0ba5471111926a96745c45695de1c1b0e9f6129bfc077e6dddabd20e3c" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 1, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "error": "it did not run" + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-first-form-error-item-inexact.json b/wai/quantum-conformance/receipts/jobs/crafted-set-first-form-error-item-inexact.json new file mode 100644 index 000000000..7c06caaae --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-first-form-error-item-inexact.json @@ -0,0 +1,133 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "53e029e74970410ccf9440d3b3fda3f77a19f89c5b058650f7d9d1d526d01b05", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "bacb15da30a91cb743efe7600e6c466ddccd4a169bb8ff7d8e3061919774ccb5", + "signature": "eae972cf2b115b22e1a536541d1f1adb3daef228a4c38bfbf43cdc6ccbb98db8b82cfa8ad0a452f8e284642684b30e2f7df7e2db8ecc528d5cb34c3a9bbb480b" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "82a0a8dba2287a9bbd2ce7d705511ed166fa37b1765b0c7a1fbc3f419caafb94" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 1, + "request": { + "seed": 9007199254740993, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "error": "it did not run" + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-first-form-error-item.json b/wai/quantum-conformance/receipts/jobs/crafted-set-first-form-error-item.json new file mode 100644 index 000000000..3b6a86f6b --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-first-form-error-item.json @@ -0,0 +1,133 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "8535543a064b51c65cbd03e7f638d8639eacd05d2c9d3da9beff017d391edc93", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "cec6e2089594df4a6cfbfb7785093d8ff43e405086f45adc742c7ccc54b2ec9d", + "signature": "bbe03bb4e82d3c7e5a4d647197cf9575437cee5e18c69dc9f1511fc725307b0bb943d5bd5d39cf00a2cb170c8f9ed52e2861e8bd9336d0f0c7c605ac37688404" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "50c93c0ba5471111926a96745c45695de1c1b0e9f6129bfc077e6dddabd20e3c" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 1, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "error": "it did not run" + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-head-changed.json b/wai/quantum-conformance/receipts/jobs/crafted-set-head-changed.json new file mode 100644 index 000000000..d241c0116 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-head-changed.json @@ -0,0 +1,196 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "64593524fe46385009353a9c265d08b1ef5add6248bec0713f8f5dd0650714bd", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 1, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "1569eb78f42db9a29a45bfe3a55d44d6888b665c9bdd150213655d23748bbcc7", + "signature": "d9fb355d4552ad7e5ed43025d4b9db64fd89a04562f6e330c85dad5143718ab472ca0d3ef7da39e7744c558710ff86631302f8c124958792434baa243d1f8202" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "dc8533cd8995ddd5d170447f7eddbf6fb54a90ef8ec8c0a32943c953e5c71adf" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 1, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "result": { + "method": "exact", + "qubits": 3, + "gates": 5, + "accuracy": { + "class": "bit-exact" + }, + "state_hash": "5f1ae3e98c0555290986daf0d573de16a34ba01d1993f13f134e0b1daf6f1ab5", + "counts": { + "000": 34, + "111": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "ea71913eeaa729b80f38c332ee03b2a2bb08d3430f37617ad64c83f1f91b78a9", + "qubits": 3, + "gates": 5 + }, + "computer": { + "kind": "simulator", + "method": "exact", + "accuracy": "bit-exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "memory_bytes": 134218112, + "work": 40 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ddeb9ad8daad97246d28081c7a3f8d3152f975185a25261ec063eaba60f3e38b", + "signature": "c2c2c23f8d939e94069ef7501507c8763203324ef122247b8b748827539ccb129da636905f457ba3a62fab97153bcbc700190e9fa516c5a52115d631a3bb6600" + } + } + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-index-swapped.json b/wai/quantum-conformance/receipts/jobs/crafted-set-index-swapped.json new file mode 100644 index 000000000..3353a6076 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-index-swapped.json @@ -0,0 +1,196 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "64593524fe46385009353a9c265d08b1ef5add6248bec0713f8f5dd0650714bd", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "1569eb78f42db9a29a45bfe3a55d44d6888b665c9bdd150213655d23748bbcc7", + "signature": "d9fb355d4552ad7e5ed43025d4b9db64fd89a04562f6e330c85dad5143718ab472ca0d3ef7da39e7744c558710ff86631302f8c124958792434baa243d1f8202" + }, + "items": [ + { + "index": 1, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "dc8533cd8995ddd5d170447f7eddbf6fb54a90ef8ec8c0a32943c953e5c71adf" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "result": { + "method": "exact", + "qubits": 3, + "gates": 5, + "accuracy": { + "class": "bit-exact" + }, + "state_hash": "5f1ae3e98c0555290986daf0d573de16a34ba01d1993f13f134e0b1daf6f1ab5", + "counts": { + "000": 34, + "111": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "ea71913eeaa729b80f38c332ee03b2a2bb08d3430f37617ad64c83f1f91b78a9", + "qubits": 3, + "gates": 5 + }, + "computer": { + "kind": "simulator", + "method": "exact", + "accuracy": "bit-exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "memory_bytes": 134218112, + "work": 40 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ddeb9ad8daad97246d28081c7a3f8d3152f975185a25261ec063eaba60f3e38b", + "signature": "c2c2c23f8d939e94069ef7501507c8763203324ef122247b8b748827539ccb129da636905f457ba3a62fab97153bcbc700190e9fa516c5a52115d631a3bb6600" + } + } + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-item-changed.json b/wai/quantum-conformance/receipts/jobs/crafted-set-item-changed.json new file mode 100644 index 000000000..d0a27465a --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-item-changed.json @@ -0,0 +1,196 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "99a8cb1c7925baf343173bec5a7073924d1c9dc113ad033becc1fce8d47801d2", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "4ea6d36ed108b9f7ef7bd2b3447b7a34ea200f4e8cc52d3a260cb879641bede6", + "signature": "961682bee4e08529dbbef3cc4f5d156dd21c5e58577a284ece9e100018ac86b99afc8c38b11ff9e4a341a954815f574385123546da10955cbfc1adff10c0c703" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "dc8533cd8995ddd5d170447f7eddbf6fb54a90ef8ec8c0a32943c953e5c71adf" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 37, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 1, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "2ce595dae237b2aacb809a5962eb4a3bfcfe5dbcc1877fa66d6bb6bb6a10181a" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "result": { + "method": "exact", + "qubits": 3, + "gates": 5, + "accuracy": { + "class": "bit-exact" + }, + "state_hash": "5f1ae3e98c0555290986daf0d573de16a34ba01d1993f13f134e0b1daf6f1ab5", + "counts": { + "000": 34, + "111": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "ea71913eeaa729b80f38c332ee03b2a2bb08d3430f37617ad64c83f1f91b78a9", + "qubits": 3, + "gates": 5 + }, + "computer": { + "kind": "simulator", + "method": "exact", + "accuracy": "bit-exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "memory_bytes": 134218112, + "work": 40 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ddeb9ad8daad97246d28081c7a3f8d3152f975185a25261ec063eaba60f3e38b", + "signature": "c2c2c23f8d939e94069ef7501507c8763203324ef122247b8b748827539ccb129da636905f457ba3a62fab97153bcbc700190e9fa516c5a52115d631a3bb6600" + } + } + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-item-not-given.json b/wai/quantum-conformance/receipts/jobs/crafted-set-item-not-given.json new file mode 100644 index 000000000..de581b23c --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-item-not-given.json @@ -0,0 +1,196 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "1139436b435a46a9278a56fea1303b9b94f8b9a9f99134d0bfd74123c9640575", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "63208ceefc5be1cbfa32bfbd0a69c28a2507428e103a0a8d223b9388bdcef255", + "signature": "e3058a04ef6d4abcc27e3c43eadb0692d029fce0a3d0acc7b0b92b1dd71e35d83fc7e50749e6994eec3e3ba6e3be935955363454ac550efcdf1ecdefd7ef1a01" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "a4254b700d33e4baf42e334fc9701dcf5dfb4400a234e4676ffc549527fcf336" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 1, + "request": { + "seed": 3, + "shots": 65, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "result": { + "method": "exact", + "qubits": 3, + "gates": 5, + "accuracy": { + "class": "bit-exact" + }, + "state_hash": "5f1ae3e98c0555290986daf0d573de16a34ba01d1993f13f134e0b1daf6f1ab5", + "counts": { + "000": 34, + "111": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "ea71913eeaa729b80f38c332ee03b2a2bb08d3430f37617ad64c83f1f91b78a9", + "qubits": 3, + "gates": 5 + }, + "computer": { + "kind": "simulator", + "method": "exact", + "accuracy": "bit-exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "memory_bytes": 134218112, + "work": 40 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ec2d404c12d9095c5b7051ec8c4d9c066dc54cd122583638a8dccfe3ba569723", + "signature": "3c64c0934e0405e4efbe5de4d7ae481fe974348a439d38f1136dac36a9bc667ebb5749c0aea2c556ded561b7355f20f3a04061f73d42616a18cf0f8605224504" + } + } + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-item-other-key.json b/wai/quantum-conformance/receipts/jobs/crafted-set-item-other-key.json new file mode 100644 index 000000000..9ea698483 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-item-other-key.json @@ -0,0 +1,196 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "64593524fe46385009353a9c265d08b1ef5add6248bec0713f8f5dd0650714bd", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "1569eb78f42db9a29a45bfe3a55d44d6888b665c9bdd150213655d23748bbcc7", + "signature": "d9fb355d4552ad7e5ed43025d4b9db64fd89a04562f6e330c85dad5143718ab472ca0d3ef7da39e7744c558710ff86631302f8c124958792434baa243d1f8202" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "dc8533cd8995ddd5d170447f7eddbf6fb54a90ef8ec8c0a32943c953e5c71adf" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "4508a07aa941707f3eb2db94c8897a80b2c1197476b6de213ac273df7d86c4ff", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "bf72b1b58815d3235c0b8cadf54ebf06f3f95256a9c4a8b8df3fac48def3d8024d348a6599749fa30c35e0a671b1438ad19577bdeb2976aba8d76750c739c806" + } + } + }, + { + "index": 1, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "result": { + "method": "exact", + "qubits": 3, + "gates": 5, + "accuracy": { + "class": "bit-exact" + }, + "state_hash": "5f1ae3e98c0555290986daf0d573de16a34ba01d1993f13f134e0b1daf6f1ab5", + "counts": { + "000": 34, + "111": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "ea71913eeaa729b80f38c332ee03b2a2bb08d3430f37617ad64c83f1f91b78a9", + "qubits": 3, + "gates": 5 + }, + "computer": { + "kind": "simulator", + "method": "exact", + "accuracy": "bit-exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "memory_bytes": 134218112, + "work": 40 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ddeb9ad8daad97246d28081c7a3f8d3152f975185a25261ec063eaba60f3e38b", + "signature": "c2c2c23f8d939e94069ef7501507c8763203324ef122247b8b748827539ccb129da636905f457ba3a62fab97153bcbc700190e9fa516c5a52115d631a3bb6600" + } + } + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-path-other.json b/wai/quantum-conformance/receipts/jobs/crafted-set-path-other.json new file mode 100644 index 000000000..865f4eda1 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-path-other.json @@ -0,0 +1,196 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "64593524fe46385009353a9c265d08b1ef5add6248bec0713f8f5dd0650714bd", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "1569eb78f42db9a29a45bfe3a55d44d6888b665c9bdd150213655d23748bbcc7", + "signature": "d9fb355d4552ad7e5ed43025d4b9db64fd89a04562f6e330c85dad5143718ab472ca0d3ef7da39e7744c558710ff86631302f8c124958792434baa243d1f8202" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 1, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "result": { + "method": "exact", + "qubits": 3, + "gates": 5, + "accuracy": { + "class": "bit-exact" + }, + "state_hash": "5f1ae3e98c0555290986daf0d573de16a34ba01d1993f13f134e0b1daf6f1ab5", + "counts": { + "000": 34, + "111": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "ea71913eeaa729b80f38c332ee03b2a2bb08d3430f37617ad64c83f1f91b78a9", + "qubits": 3, + "gates": 5 + }, + "computer": { + "kind": "simulator", + "method": "exact", + "accuracy": "bit-exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "memory_bytes": 134218112, + "work": 40 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ddeb9ad8daad97246d28081c7a3f8d3152f975185a25261ec063eaba60f3e38b", + "signature": "c2c2c23f8d939e94069ef7501507c8763203324ef122247b8b748827539ccb129da636905f457ba3a62fab97153bcbc700190e9fa516c5a52115d631a3bb6600" + } + } + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-root-other.json b/wai/quantum-conformance/receipts/jobs/crafted-set-root-other.json new file mode 100644 index 000000000..48c8da927 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-root-other.json @@ -0,0 +1,196 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 2 items, run one after another in one process (exact, stabilizer)", + "estimate": { + "memory_bytes": 134218112, + "work": 176 + }, + "request": { + "seed": 3, + "shots": 64, + "programs": [ + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + ] + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 2, + "failed": 0, + "root": "abababababababababababababababababababababababababababababababab", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "9f0d1b47f819c3fe8aa4c9195aa2fd53b78bbd419cd40d76a500375c1db8491d", + "signature": "46717c5d4b349a221b54a2ecf98fba1981c3600f3c04da649df7ef825a30a000021a2b1542814b65a8e6c8ec3cc110acb4e30c662250212126c98950128ffc06" + }, + "items": [ + { + "index": 0, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n" + }, + "path": [ + "dc8533cd8995ddd5d170447f7eddbf6fb54a90ef8ec8c0a32943c953e5c71adf" + ], + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 36, + "11": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a", + "qubits": 2, + "gates": 2 + }, + "computer": { + "kind": "simulator", + "method": "stabilizer", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "memory_bytes": 67112966, + "work": 136 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "56ab6900ccaf358ec738550e31f1e579513b3984a0a8b46081645daa1f6acb2e", + "signature": "15138c0199864735e0b01b616ae4ccf818134aab5182d42a9b42f51a49b02cc0661252d626c12e7933c0eee8d0d69551bfe3eb58afa637b3c8ab170ac98ac604" + } + } + }, + { + "index": 1, + "request": { + "seed": 3, + "shots": 64, + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n" + }, + "path": [ + "ebc5c50b117c6f40d6fdf4cf919240b73072d25cc9806c9c76fd0fa967368cbc" + ], + "status": "done", + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "result": { + "method": "exact", + "qubits": 3, + "gates": 5, + "accuracy": { + "class": "bit-exact" + }, + "state_hash": "5f1ae3e98c0555290986daf0d573de16a34ba01d1993f13f134e0b1daf6f1ab5", + "counts": { + "000": 34, + "111": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "ea71913eeaa729b80f38c332ee03b2a2bb08d3430f37617ad64c83f1f91b78a9", + "qubits": 3, + "gates": 5 + }, + "computer": { + "kind": "simulator", + "method": "exact", + "accuracy": "bit-exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 3, + "plan": { + "method": "exact", + "why": "bit-exact state vector of 3 qubits; not stabilizer: stabilizer needs a Clifford circuit: T is not a Clifford gate", + "memory_bytes": 134218112, + "work": 40 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "ddeb9ad8daad97246d28081c7a3f8d3152f975185a25261ec063eaba60f3e38b", + "signature": "c2c2c23f8d939e94069ef7501507c8763203324ef122247b8b748827539ccb129da636905f457ba3a62fab97153bcbc700190e9fa516c5a52115d631a3bb6600" + } + } + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-sweep-inexact.json b/wai/quantum-conformance/receipts/jobs/crafted-set-sweep-inexact.json new file mode 100644 index 000000000..db8c97680 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-sweep-inexact.json @@ -0,0 +1,289 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 3 items, run one after another in one process (statevector)", + "estimate": { + "memory_bytes": 268437025, + "work": 6 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ninput float theta;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n", + "shots": 64, + "seed": 9, + "sweep": { + "theta": [ + 9007199254740993, + 0.5, + 1.5 + ] + } + }, + "result": { + "format": "wai.quantum.set/1", + "count": 3, + "done": 3, + "failed": 0, + "root": "9f35eeaa9a93eeb51f9fdd91cfb1da9c6e77bb4f02733cbf0ee63c3c94a3014a", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "90583331aa5a987186f6b7d6d2391e66fda8baadcc5e45f56cdc1de9d3fe2930", + "signature": "5c3670f4975733bfdd6db5a84a2ee44c8363bb070563fd62019831d7a6379c6966bd86ff6e8778eb1cb8619334a98d76b8b3a0118260feaf86f4c01ead160804" + }, + "items": [ + { + "index": 0, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nconst float theta = 0.25;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n", + "shots": 64, + "seed": 9 + }, + "path": [ + "95043424c186bd5641172d73e3db14c529870ad8c4ad31c12b1f7c1641f52330", + "a3cb6daf6e4aeea406af8b15fed951ccea11461ff000d97878abe038e39f1fc9" + ], + "point": { + "theta": 0.25 + }, + "status": "done", + "method": "statevector", + "why": "dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.25) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.25 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]", + "result": { + "method": "statevector", + "qubits": 1, + "gates": 1, + "accuracy": { + "class": "exact", + "fusion": 3 + }, + "counts": { + "0": 62, + "1": 2 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "c95e6d9dad59b9d07544b09a9b1b826b4786c9591e56fccf66c1373498d5b18e", + "qubits": 1, + "gates": 1 + }, + "computer": { + "kind": "simulator", + "method": "statevector", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 9, + "plan": { + "method": "statevector", + "why": "dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.25) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.25 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]", + "memory_bytes": 268437025, + "work": 2 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "bdcc96b2490c1f61e2d70162637fd857aeb22bb508bda30a818addc47126374e", + "signature": "7621530b8fa28c96ce9f5ff0137694b52ef49937bc49f282df44cf17458415211dafbdc67de7a592b2a8a6a7a97888fdbc933aed3aa172c078bbe7afdfbb640c" + } + } + }, + { + "index": 1, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nconst float theta = 0.5;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n", + "shots": 64, + "seed": 9 + }, + "path": [ + "7d2993808de20a704e43f32f8050c5b5d4c4cab6d9ac9a5b1eb4639dc456c517", + "a3cb6daf6e4aeea406af8b15fed951ccea11461ff000d97878abe038e39f1fc9" + ], + "point": { + "theta": 0.5 + }, + "status": "done", + "method": "statevector", + "why": "dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.5) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.5 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]", + "result": { + "method": "statevector", + "qubits": 1, + "gates": 1, + "accuracy": { + "class": "exact", + "fusion": 3 + }, + "counts": { + "0": 60, + "1": 4 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "29c4daa711120eaab818a9406a4697287d50c22222762fca04a8e5db0aa89a77", + "qubits": 1, + "gates": 1 + }, + "computer": { + "kind": "simulator", + "method": "statevector", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 9, + "plan": { + "method": "statevector", + "why": "dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.5) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.5 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]", + "memory_bytes": 268437025, + "work": 2 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "7088942d5e36e3cc86977bbd096704e6cceac557730b6c8ce415720c093b554e", + "signature": "4499dda0bf56a8829a6a166fc0518f014ed9bf6c2545869583888c82060a134ae9f1f67604c30507c83c650719f86f612fe74ddacca29cb6ae654ea928375205" + } + } + }, + { + "index": 2, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nconst float theta = 1.5;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n", + "shots": 64, + "seed": 9 + }, + "path": [ + "10a868a06061e0d1f65a84fe4debe8878b4ef8898cea01b0661398b0064125dc", + "e82c037670d3dea4c2fdfbeaab601de68e80f138f5b3416c803835c74246de49" + ], + "point": { + "theta": 1.5 + }, + "status": "done", + "method": "statevector", + "why": "dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(1.5) is not a Clifford gate; exact: exact needs dyadic angles: the angle 1.5 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]", + "result": { + "method": "statevector", + "qubits": 1, + "gates": 1, + "accuracy": { + "class": "exact", + "fusion": 3 + }, + "counts": { + "0": 36, + "1": 28 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "59827680d2db446f6ce85a185c00be9f351895dfe357d8d77aa619cb4f3ab49e", + "qubits": 1, + "gates": 1 + }, + "computer": { + "kind": "simulator", + "method": "statevector", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 9, + "plan": { + "method": "statevector", + "why": "dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(1.5) is not a Clifford gate; exact: exact needs dyadic angles: the angle 1.5 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]", + "memory_bytes": 268437025, + "work": 2 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "2c2c81405655d4ad81771ba70c71e2d29fe284d51a203d43b86977155aa10090", + "signature": "b9c3a4397dff4c27ef4825e66fe6be49f8bf66aec82b78d150da6705758d17687147bed8c1c2e28770c6e37d0fb7d7895922442109368449fa3d12fe9a28670f" + } + } + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/crafted-set-sweep-short.json b/wai/quantum-conformance/receipts/jobs/crafted-set-sweep-short.json new file mode 100644 index 000000000..13b71d914 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/crafted-set-sweep-short.json @@ -0,0 +1,206 @@ +{ + "status": "done", + "method": "set", + "why": "a set of 3 items, run one after another in one process (statevector)", + "estimate": { + "memory_bytes": 268437025, + "work": 6 + }, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ninput float theta;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n", + "shots": 64, + "seed": 9, + "sweep": { + "theta": [ + 0.25, + 0.5, + 1.5 + ] + } + }, + "result": { + "format": "wai.quantum.set/1", + "count": 2, + "done": 3, + "failed": 0, + "root": "e82c037670d3dea4c2fdfbeaab601de68e80f138f5b3416c803835c74246de49", + "energy": { + "unmetered": "the command line does not meter a run's energy" + }, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.set/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "6016506255a039d5eb9cdeee490677aaf5f26032485993dfbca24a0ba59761ad", + "signature": "61ed049b6bc4d761376391fc66a55bb0fc93a9a7242a226d1d451f81d5260e07dfd347c54c09e319d2d4015cd892d59aa23a235b0708c55d6ab936f959c1cf06" + }, + "items": [ + { + "index": 0, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nconst float theta = 0.25;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n", + "shots": 64, + "seed": 9 + }, + "path": [ + "95043424c186bd5641172d73e3db14c529870ad8c4ad31c12b1f7c1641f52330" + ], + "point": { + "theta": 0.25 + }, + "status": "done", + "method": "statevector", + "why": "dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.25) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.25 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]", + "result": { + "method": "statevector", + "qubits": 1, + "gates": 1, + "accuracy": { + "class": "exact", + "fusion": 3 + }, + "counts": { + "0": 62, + "1": 2 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "c95e6d9dad59b9d07544b09a9b1b826b4786c9591e56fccf66c1373498d5b18e", + "qubits": 1, + "gates": 1 + }, + "computer": { + "kind": "simulator", + "method": "statevector", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 9, + "plan": { + "method": "statevector", + "why": "dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.25) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.25 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]", + "memory_bytes": 268437025, + "work": 2 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "bdcc96b2490c1f61e2d70162637fd857aeb22bb508bda30a818addc47126374e", + "signature": "7621530b8fa28c96ce9f5ff0137694b52ef49937bc49f282df44cf17458415211dafbdc67de7a592b2a8a6a7a97888fdbc933aed3aa172c078bbe7afdfbb640c" + } + } + }, + { + "index": 1, + "request": { + "qasm": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nconst float theta = 0.5;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n", + "shots": 64, + "seed": 9 + }, + "path": [ + "7d2993808de20a704e43f32f8050c5b5d4c4cab6d9ac9a5b1eb4639dc456c517" + ], + "point": { + "theta": 0.5 + }, + "status": "done", + "method": "statevector", + "why": "dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.5) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.5 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]", + "result": { + "method": "statevector", + "qubits": 1, + "gates": 1, + "accuracy": { + "class": "exact", + "fusion": 3 + }, + "counts": { + "0": 60, + "1": 4 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "elapsed_ms": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "provenance": { + "schema": "wai.quantum.provenance/1", + "circuit": { + "program": "29c4daa711120eaab818a9406a4697287d50c22222762fca04a8e5db0aa89a77", + "qubits": 1, + "gates": 1 + }, + "computer": { + "kind": "simulator", + "method": "statevector", + "accuracy": "exact" + }, + "compilation": { + "asked": false, + "status": "not-applicable", + "why": "the job asked for no compile: the program ran as the reader read it, on the method's own gates" + }, + "execution": { + "shots": 64, + "seed": 9, + "plan": { + "method": "statevector", + "why": "dense double-precision state vector of 1 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.5) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.5 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32) [Q500]", + "memory_bytes": 268437025, + "work": 2 + } + }, + "software": { + "engine": "wai-quantum 0.8.1", + "host": "wai-quantum 0.8.1" + } + }, + "energy": { + "unmetered": "an item of a set: the set's own figure covers every item together, since they run in one process" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "7088942d5e36e3cc86977bbd096704e6cceac557730b6c8ce415720c093b554e", + "signature": "4499dda0bf56a8829a6a166fc0518f014ed9bf6c2545869583888c82060a134ae9f1f67604c30507c83c650719f86f612fe74ddacca29cb6ae654ea928375205" + } + } + } + ] + } +} diff --git a/wai/quantum-conformance/receipts/jobs/first-form-service-observables.json b/wai/quantum-conformance/receipts/jobs/first-form-service-observables.json new file mode 100644 index 000000000..965c6389e --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/first-form-service-observables.json @@ -0,0 +1 @@ +{"id":"9cfbecc55c4ccf1395a066877cc04b1e","status":"done","method":"statevector","why":"dense double-precision state vector of 2 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rx(0.1) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.1 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32)","estimate":{"memory_bytes":268435522,"work":12},"request":{"qasm":"OPENQASM 3.0;\nqubit[2] q;\nh q[0];\nrx(0.1) q[1];\ncx q[0], q[1];\n","shots":0,"observables":["Z0 Z1","X0",{"pauli":"X1","coeff":0.5}],"seed":3},"submitted_ms":1793920879593,"started_ms":1793920879593,"finished_ms":1793920879798,"peak_rss_bytes":8323072,"result":{"method":"statevector","qubits":2,"gates":3,"accuracy":{"class":"exact","fusion":3},"expectations":[{"observable":"Z0 Z1","coeff":1.0,"value":0.9950041652780258},{"observable":"X0","coeff":1.0,"value":0.0},{"observable":"X1","coeff":0.5,"value":0.0}],"elapsed_ms":0,"receipt":{"alg":"ed25519","key":"ac2ca498780f7912aa54203d0c37f64f4c0db82fbed2fe4e4646cc48790cdf9a","digest":"b1d2ffbb05afe6ed5b76ca338d22fcf7708b24373b7d852c44ab59e92970aa6c","signature":"b29c8d66a1317b30e975d79db27b14969418844bf6b9decc4f55df0a2c60a1028b790b8b86a2c41c41ee10c1686dd3b1955889ef0bb96742a01bfee030c6b307"}}} diff --git a/wai/quantum-conformance/receipts/jobs/first-form-service-run.json b/wai/quantum-conformance/receipts/jobs/first-form-service-run.json new file mode 100644 index 000000000..52f81aa53 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/first-form-service-run.json @@ -0,0 +1 @@ +{"id":"29b08143c58a26d2f99295cdc730126a","status":"done","method":"statevector","why":"dense double-precision state vector of 2 qubits; not stabilizer: stabilizer needs a Clifford circuit: Rz(0.3) is not a Clifford gate; exact: exact needs dyadic angles: the angle 0.3 is not a dyadic multiple of 2π (2πm/2^k, k ≤ 32)","estimate":{"memory_bytes":268459522,"work":12},"request":{"qasm":"OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\nrz(0.3) q[0];\ncx q[0], q[1];\nc = measure q;\n","shots":1000,"seed":7},"submitted_ms":1793920879324,"started_ms":1793920879324,"finished_ms":1793920879534,"peak_rss_bytes":880640,"result":{"method":"statevector","qubits":2,"gates":3,"accuracy":{"class":"exact","fusion":3},"counts":{"00":525,"11":475},"distinct_outcomes":2,"omitted_shots":0,"elapsed_ms":0,"receipt":{"alg":"ed25519","key":"ac2ca498780f7912aa54203d0c37f64f4c0db82fbed2fe4e4646cc48790cdf9a","digest":"7f1caccfbb41a4281cbf5adfe92ca6674afe464d5fa94575074169636b0a6dd1","signature":"cac1ebd822c9c3a975cfc3f46aedf9724be9e1ca37a1206b8bbd7309a78e359cba25852ca6249880cab1696b5a0775456b1576caeeee0531bbf138fbbafcb40f"}}} diff --git a/wai/quantum-conformance/receipts/jobs/fixture-compile-legacy.json b/wai/quantum-conformance/receipts/jobs/fixture-compile-legacy.json new file mode 100644 index 000000000..c001528d7 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/fixture-compile-legacy.json @@ -0,0 +1,488 @@ +{ + "request": { + "qasm": "OPENQASM 3.0; qubit[2] q; h q[0]; rz(0.3) q[0]; cx q[0], q[1]; ry(1.1) q[1];", + "epsilon": 1e-6, + "level": 2 + }, + "result": { + "ok": true, + "options": { + "epsilon": 1e-6, + "level": 2, + "target": "clifford_t" + }, + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\ntdg q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\nh q[0];\ns q[0];\nt q[0];\ncx q[0], q[1];\nh q[1];\nx q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\nt q[1];\nh q[1];\ns q[1];\nt q[1];\nh q[1];\nc[0] = measure q[0];\nc[1] = measure q[1];\n", + "certificate": { + "format": "wai.quantum.compile/1", + "target": "clifford_t", + "level": 2, + "source": { + "hash": "3088646de076cdf015688f9638832413050d5d15b4cf27a358977efed0021634", + "qubits": 2, + "counts": { + "gates": 4, + "one_qubit": 3, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 4 + } + }, + "output": { + "hash": "43a412104c45393241bd9741012dbd18bb08255fddeef96bf5b7144bd78a8dc9", + "counts": { + "gates": 319, + "one_qubit": 318, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 129, + "rotations": 0, + "depth": 319 + } + }, + "epsilon": { + "budget": 1e-6, + "rotations": 2, + "each": 5e-7, + "bound": 6.053576919651645e-7 + }, + "passes": [ + { + "pass": "simplify", + "before": { + "gates": 4, + "one_qubit": 3, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 4 + }, + "after": { + "gates": 4, + "one_qubit": 3, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 4 + }, + "check": { + "how": "rewrites, each checked on the qubits it touches by tableau or on every basis state", + "verdict": "exact", + "steps": 0 + } + }, + { + "pass": "lower", + "before": { + "gates": 4, + "one_qubit": 3, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 4 + }, + "after": { + "gates": 8, + "one_qubit": 7, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 8 + }, + "check": { + "how": "each kind of gate against its decomposition, on its own qubits", + "verdict": "exact", + "checked": 1, + "distance": 5.551115123125783e-17 + } + }, + { + "pass": "simplify", + "before": { + "gates": 8, + "one_qubit": 7, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 8 + }, + "after": { + "gates": 6, + "one_qubit": 5, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 6 + }, + "check": { + "how": "rewrites, each checked on the qubits it touches by tableau or on every basis state", + "verdict": "exact", + "steps": 1 + } + }, + { + "pass": "resynth_2q", + "before": { + "gates": 6, + "one_qubit": 5, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 6 + }, + "after": { + "gates": 6, + "one_qubit": 5, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 6 + }, + "check": { + "how": "each block compared with its replacement on their 4×4 matrices", + "verdict": "exact", + "blocks": 0, + "distance": 0.0 + } + }, + { + "pass": "lower", + "before": { + "gates": 6, + "one_qubit": 5, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 6 + }, + "after": { + "gates": 8, + "one_qubit": 7, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 8 + }, + "check": { + "how": "each kind of gate against its decomposition, on its own qubits", + "verdict": "exact", + "checked": 1, + "distance": 5.551115123125783e-17 + } + }, + { + "pass": "resynth_1q", + "before": { + "gates": 8, + "one_qubit": 7, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 8 + }, + "after": { + "gates": 8, + "one_qubit": 7, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 8 + }, + "check": { + "how": "Clifford+T runs multiplied out in ring arithmetic against their normal form; other runs compared on their 2×2 matrices", + "verdict": "proof", + "exact_runs": 0, + "measured_runs": 0, + "distance": 0.0 + } + }, + { + "pass": "phase_fold", + "before": { + "gates": 8, + "one_qubit": 7, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 8 + }, + "after": { + "gates": 8, + "one_qubit": 7, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 8 + }, + "check": { + "how": "the phase polynomial recomputed: the same gates once the phases are taken out, and the same angle on every parity to the rounding of the sums", + "verdict": "exact", + "merged": 0 + } + }, + { + "pass": "simplify", + "before": { + "gates": 8, + "one_qubit": 7, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 8 + }, + "after": { + "gates": 6, + "one_qubit": 5, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 6 + }, + "check": { + "how": "rewrites, each checked on the qubits it touches by tableau or on every basis state", + "verdict": "exact", + "steps": 1 + } + }, + { + "pass": "lower", + "before": { + "gates": 6, + "one_qubit": 5, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 6 + }, + "after": { + "gates": 8, + "one_qubit": 7, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 8 + }, + "check": { + "how": "each kind of gate against its decomposition, on its own qubits", + "verdict": "exact", + "checked": 1, + "distance": 5.551115123125783e-17 + } + }, + { + "pass": "approximate", + "before": { + "gates": 8, + "one_qubit": 7, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 0, + "rotations": 2, + "depth": 8 + }, + "after": { + "gates": 335, + "one_qubit": 334, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 129, + "rotations": 0, + "depth": 335 + }, + "check": { + "how": "each rotation's Clifford+T word multiplied out in exact ring arithmetic, and its operator-norm distance to the rotation recomputed from the word in fixed point and rounded up", + "verdict": "approximate", + "rotations": 2, + "epsilon_each": 5e-7, + "t_count": 129, + "distance": 6.053576919651645e-7 + } + }, + { + "pass": "resynth_1q", + "before": { + "gates": 335, + "one_qubit": 334, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 129, + "rotations": 0, + "depth": 335 + }, + "after": { + "gates": 320, + "one_qubit": 319, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 129, + "rotations": 0, + "depth": 320 + }, + "check": { + "how": "Clifford+T runs multiplied out in ring arithmetic against their normal form; other runs compared on their 2×2 matrices", + "verdict": "proof", + "exact_runs": 2, + "measured_runs": 0, + "distance": 0.0 + } + }, + { + "pass": "phase_fold", + "before": { + "gates": 320, + "one_qubit": 319, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 129, + "rotations": 0, + "depth": 320 + }, + "after": { + "gates": 319, + "one_qubit": 318, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 129, + "rotations": 0, + "depth": 319 + }, + "check": { + "how": "the phase polynomial recomputed: the same gates once the phases are taken out, and the same angle on every parity, in whole eighths of a turn", + "verdict": "proof", + "merged": 60 + } + }, + { + "pass": "simplify", + "before": { + "gates": 319, + "one_qubit": 318, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 129, + "rotations": 0, + "depth": 319 + }, + "after": { + "gates": 132, + "one_qubit": 131, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 129, + "rotations": 0, + "depth": 132 + }, + "check": { + "how": "rewrites, each checked on the qubits it touches by tableau or on every basis state", + "verdict": "exact", + "steps": 123 + } + }, + { + "pass": "lower", + "before": { + "gates": 132, + "one_qubit": 131, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 129, + "rotations": 0, + "depth": 132 + }, + "after": { + "gates": 319, + "one_qubit": 318, + "two_qubit": 1, + "three_qubit": 0, + "cnots": 1, + "t_count": 129, + "rotations": 0, + "depth": 319 + }, + "check": { + "how": "each kind of gate against its decomposition, on its own qubits", + "verdict": "exact", + "checked": 3, + "distance": 2.4729823389985603e-16 + } + } + ], + "rotations": [ + { + "angle": 0.3, + "t_count": 65, + "distance": 3.7220465355042864e-7, + "k": 33 + }, + { + "angle": 1.1, + "t_count": 64, + "distance": 2.3315303841473587e-7, + "k": 33 + } + ], + "whole": { + "how": "the dense unitaries of the whole program and the whole output", + "distance": 5.153537402073438e-7, + "verdict": "approximate" + }, + "verdict": "approximate", + "reading": "The output equals the program up to a global phase to within 6.053576919651645e-7 in the operator norm (the budget was 1e-6). On any input, the output distribution is then within bound of the program's in total variation (so is every outcome's probability), and an observable's expectation within 2·bound·‖O‖. Hashes are BLAKE3 of the program as read (written out as OpenQASM 3) and of the output text." + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "fd1724385aa0c75b64fb78cd602fa1d991fdebf76b13c58ed702eac835e9f618", + "digest": "5ca60a55f23daf7797cf66d41c5065f32b8acfbc33aebe431603e260ae3b34c7", + "signature": "6a8fc7a1b767579a30308779c070dbeb2811c2d3d7e7d3d4c2b38baad13b8a36804258a3d4df55b25bfef80ecc9d4f8a8610a332bf9e8f7bf1b6112ffca7a80f" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/fixture-job-legacy.json b/wai/quantum-conformance/receipts/jobs/fixture-job-legacy.json new file mode 100644 index 000000000..d4c3c89ad --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/fixture-job-legacy.json @@ -0,0 +1,50 @@ +{ + "status": "done", + "method": "stabilizer", + "why": "Clifford circuit: the stabilizer tableau of 2 qubits", + "estimate": { + "memory_bytes": 67115270, + "work": 212 + }, + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 100, + "seed": 7, + "observables": [ + "Z0 Z1" + ] + }, + "result": { + "method": "stabilizer", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "exact" + }, + "counts": { + "00": 56, + "11": 44 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "outcome_dimension": 1, + "expectations": [ + { + "observable": "Z0 Z1", + "coeff": 1.0, + "value": 1.0 + } + ], + "elapsed_ms": 0, + "energy": { + "unmetered": "a fixture sealed before results named their engine and schema" + }, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "66be7e332c7a453332bd9d0a7f7db055f5c5ef1a06ada66d98b39fb6810c473a", + "digest": "5be5601f24b2f0cd11bd46cd66789d473d242a3b3ddb97b3e67a4b3ef7c745d1", + "signature": "f5a203e7ae005769b26088a290e52c710a95ac5db00d2bf6ec506f05570928ea228663661451331893f90fe706c5e4ade5e3bfbb24434a27c1f75e536d802704" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/fixture-provider-job-legacy.json b/wai/quantum-conformance/receipts/jobs/fixture-provider-job-legacy.json new file mode 100644 index 000000000..c0475b24f --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/fixture-provider-job-legacy.json @@ -0,0 +1,65 @@ +{ + "status": "done", + "method": "provider", + "why": "a fixture", + "request": { + "qasm": "OPENQASM 3.0; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;", + "provider": "lab", + "shots": 64, + "seed": 1 + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts measured on the device the provider names, as it returned them, with no mitigation; not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them" + }, + "shots": 64, + "provider": { + "name": "lab", + "kind": "process", + "protocol": "wai.quantum.provider/1", + "device": "reference", + "provenance": "simulator" + }, + "calibration": "snap-1", + "compiled": { + "basis": "rzsxxcz", + "program": "00" + }, + "job": { + "id": "j-1", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.03125, + "hellinger_fidelity": 0.9990224819584785, + "sampling_tvd_expected": 0.04986778505017909, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the measured counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's error, or a program the device did not run as sent" + }, + "cost": { + "free": true + }, + "energy": { + "unmetered": "the work ran at the provider" + }, + "counts": { + "11": 34, + "00": 30 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "66be7e332c7a453332bd9d0a7f7db055f5c5ef1a06ada66d98b39fb6810c473a", + "digest": "5e59c414d36bcf450cd25581a726670cbf68879ac52b991f7cec9da6e37510d9", + "signature": "3ae62f5cc67d647a7cf0c4251adfd76c3718e4b26153b21a681a3542a51732cca7bb22c346ea54492b2c9357749507b7eba6894dad30348f2f8b9ebbbc055d02" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/scripted-cloud-tasks-processor-probabilities.json b/wai/quantum-conformance/receipts/jobs/scripted-cloud-tasks-processor-probabilities.json new file mode 100644 index 000000000..d6e457840 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/scripted-cloud-tasks-processor-probabilities.json @@ -0,0 +1,126 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, cloud-tasks:Line (cloud-tasks): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "cloud-tasks", + "protocol": "cloud-tasks", + "device": "cloud-tasks:Line", + "provenance": "silicon", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "4f18897e3839ecb995e7baa9eb493c654ef257b47f3056b3925be06cf1517964" + }, + "calibration": "2026-11-05T00:00:00Z", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "4", + "1": "7" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "arn:task:0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": false, + "amount": "0.470000", + "currency": "USD", + "unit": "shot", + "note": "the device's price per shot, which its API states, and the service's fee per task of 0.30 USD, from its price list of 2026-11-05, which its API does not state" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "probabilities", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "action", + "clientToken", + "deviceArn", + "outputS3Bucket", + "outputS3KeyPrefix", + "shots" + ], + "settings": { + "verbatim_box": true + } + }, + "verbatim": true, + "note": "the processor's gates go in the service's verbatim box on its physical qubits, so the service neither remaps nor optimises them; the task sets no device parameters, so mitigation, twirling and decoupling are the service's defaults", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "off", + "optimisation": "off" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "837fa0155e4cdd585adcf3c3eb90f76c1271aeb5ee90cb093795e71f63b43acc", + "signature": "96ce3de20dc02b0b04e601ca362c7d21de69999ad4f0f1ed1f699c67e7cf44fdc005f2e43c8a3feaa43872ea12b96e1522a00d79b28417aba9814760c4d02806" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/scripted-cloud-tasks-processor.json b/wai/quantum-conformance/receipts/jobs/scripted-cloud-tasks-processor.json new file mode 100644 index 000000000..e9d285706 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/scripted-cloud-tasks-processor.json @@ -0,0 +1,126 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, cloud-tasks:Line (cloud-tasks): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "cloud-tasks", + "protocol": "cloud-tasks", + "device": "cloud-tasks:Line", + "provenance": "silicon", + "qubits": 5, + "basis": "rzsxxcz", + "describe": "4f18897e3839ecb995e7baa9eb493c654ef257b47f3056b3925be06cf1517964" + }, + "calibration": "2026-11-05T00:00:00Z", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "4", + "1": "7" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "arn:task:0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": false, + "amount": "0.470000", + "currency": "USD", + "unit": "shot", + "note": "the device's price per shot, which its API states, and the service's fee per task of 0.30 USD, from its price list of 2026-11-05, which its API does not state" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "action", + "clientToken", + "deviceArn", + "outputS3Bucket", + "outputS3KeyPrefix", + "shots" + ], + "settings": { + "verbatim_box": true + } + }, + "verbatim": true, + "note": "the processor's gates go in the service's verbatim box on its physical qubits, so the service neither remaps nor optimises them; the task sets no device parameters, so mitigation, twirling and decoupling are the service's defaults", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "off", + "optimisation": "off" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "13beafcf0871c43e37da77098ad13b2a3d510b29a3e7c00fd7b441a22d3cdc9c", + "signature": "f6ba815b4a6ae9f7fa771f017ea58748b1deb9578937f33e0ef3db82a5f98a2615fc41ee0da274586cbd2169d525fff3c8e8a9f3f0791234e8ff2127462f4b08" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/scripted-cloud-tasks-simulator.json b/wai/quantum-conformance/receipts/jobs/scripted-cloud-tasks-simulator.json new file mode 100644 index 000000000..7babea5c9 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/scripted-cloud-tasks-simulator.json @@ -0,0 +1,125 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, cloud-tasks:SIM (cloud-tasks): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "cloud-tasks", + "protocol": "cloud-tasks", + "device": "cloud-tasks:SIM", + "provenance": "simulator:the service's SIM simulator", + "qubits": 34, + "basis": "rzsxxcz", + "describe": "d052d88256742b435f059d894f7ef4287325f0516a445bd0d689a4e2c4157deb" + }, + "calibration": "2026-11-05T00:00:00Z", + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "0", + "1": "1" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "arn:task:0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "free": false, + "amount": "0.003750", + "currency": "USD", + "unit": "minute", + "note": "the device's price for its three-second minimum, at least; the time a task takes is not known before it runs", + "at_least": true + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "action", + "clientToken", + "deviceArn", + "outputS3Bucket", + "outputS3KeyPrefix", + "shots" + ], + "settings": {} + }, + "verbatim": false, + "note": "a simulator's task: no verbatim box and no device parameters, so every step is the service's default", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "accc9158e50aad0ab52ba3085f708b435edc7ecbef6706ca0918d3a262cd82d9", + "signature": "f3e53ae7f2de223f2c252f5f03dd0d6bec2b297238b9798bda8644cbc7a35488bd56f36aa0250e2e13c24f272b64636153059e394d8563cfa5647dcf308f780c" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/scripted-native-turns-noise.json b/wai/quantum-conformance/receipts/jobs/scripted-native-turns-noise.json new file mode 100644 index 000000000..e9fac190a --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/scripted-native-turns-noise.json @@ -0,0 +1,122 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, native-turns:simulator (native-turns): compiled exactly to gpizz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "native-turns", + "protocol": "native-turns", + "device": "native-turns:simulator", + "provenance": "simulator:native-turns simulator, noise a-device", + "qubits": 8, + "basis": "gpizz", + "describe": "cbdde960349ff6521b6956cf80e22b5fda69ef73fa1cea44f9dec589fb8f9acf" + }, + "calibration": null, + "compiled": { + "basis": "gpizz", + "program": "7bfa03b310e68ffe2cf1357652f9e6dadd812c852855b3ba0f2864943bbea9ff", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ngate gpi(phi) a { rz(-2*pi*phi) a; x a; rz(2*pi*phi) a; }\ngate gpi2(phi) a { rz(-2*pi*phi) a; rx(pi/2) a; rz(2*pi*phi) a; }\ngate zz(theta) a, b { cx a, b; rz(2*pi*theta) b; cx a, b; }\nbit[2] c;\ngpi2(0.75) $0;\ngpi2(0.75) $1;\nzz(0.25) $0, $1;\ngpi2(0.5) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 4, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "turns-0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait", + "service_kwh": 0.0, + "service_kwh_is": "the provider's own figure, not a measurement here" + }, + "counts_source": "probabilities", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "backend", + "input", + "noise", + "settings", + "shots", + "type" + ], + "settings": { + "/input/gateset": "native", + "/settings/error_mitigation/debiasing": false, + "/noise/model": "a-device" + } + }, + "verbatim": false, + "note": "debiasing is sent off and the program goes in the service's native gate set; the request sets nothing for twirling, decoupling, remapping or optimisation, so the service's defaults stand", + "mitigation": "off", + "twirling": "default", + "decoupling": "default", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "7e67182270614ca433740da41f6f6a1d5c36bc2f7f411eba0570b3f5064c8fe2", + "signature": "9ed91e41fe8f59420acbe20b9324c42e2902047285be78bb74659411fa33d18fb93ba10fdf0e1ff31fe1ab49ab7c9b53c3ef645bcccdde2bdaf232e7ee38160a" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/scripted-native-turns.json b/wai/quantum-conformance/receipts/jobs/scripted-native-turns.json new file mode 100644 index 000000000..04b011193 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/scripted-native-turns.json @@ -0,0 +1,120 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, native-turns:simulator (native-turns): compiled exactly to gpizz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "simulated", + "note": "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "native-turns", + "protocol": "native-turns", + "device": "native-turns:simulator", + "provenance": "simulator:native-turns simulator, noise ideal", + "qubits": 8, + "basis": "gpizz", + "describe": "93a83ea1002d3b86cfd40a37a5b7db3b3e4f4e39ef2bfac05e248495e2c24278" + }, + "calibration": null, + "compiled": { + "basis": "gpizz", + "program": "7bfa03b310e68ffe2cf1357652f9e6dadd812c852855b3ba0f2864943bbea9ff", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ngate gpi(phi) a { rz(-2*pi*phi) a; x a; rz(2*pi*phi) a; }\ngate gpi2(phi) a { rz(-2*pi*phi) a; rx(pi/2) a; rz(2*pi*phi) a; }\ngate zz(theta) a, b { cx a, b; rz(2*pi*theta) b; cx a, b; }\nbit[2] c;\ngpi2(0.75) $0;\ngpi2(0.75) $1;\nzz(0.25) $0, $1;\ngpi2(0.5) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 4, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "turns-0", + "polls": 0 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait", + "service_kwh": 0.0, + "service_kwh_is": "the provider's own figure, not a measurement here" + }, + "counts_source": "probabilities", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "backend", + "input", + "settings", + "shots", + "type" + ], + "settings": { + "/input/gateset": "native", + "/settings/error_mitigation/debiasing": false + } + }, + "verbatim": false, + "note": "debiasing is sent off and the program goes in the service's native gate set; the request sets nothing for twirling, decoupling, remapping or optimisation, so the service's defaults stand", + "mitigation": "off", + "twirling": "default", + "decoupling": "default", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "3c3ca83819d83b2bba804e63cd19adf677a221c843127b6ec1a2f6dd442a2fc3", + "signature": "0ceb2acf8f7cb9f7cfc79543b54428da69a63e728f0e164f15ee5c0fa50cff770ec30321342f9070bf264e449b796c464e9e8085bafb7e63c60bcf75b1c0a70d" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/scripted-prx-circuits.json b/wai/quantum-conformance/receipts/jobs/scripted-prx-circuits.json new file mode 100644 index 000000000..025de6abe --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/scripted-prx-circuits.json @@ -0,0 +1,116 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, prx-circuits:test-qc (prx-circuits): compiled exactly to prxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "prx-circuits", + "protocol": "prx-circuits", + "device": "prx-circuits:test-qc", + "provenance": "silicon", + "qubits": 5, + "basis": "prxcz", + "describe": "1f6d996884468f37481db4e20932d6c0ecb84ebbbf4232f734080c61ab9fae66" + }, + "calibration": null, + "compiled": { + "basis": "prxcz", + "program": "12123632bbbc70df6b58c3103f50479a72211ba109a01d9e3a581c703f160dad", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\ngate prx(theta, phi) a { rz(-phi) a; rx(theta) a; rz(phi) a; }\nbit[2] c;\nprx(pi/2, -pi/2) $0;\nprx(pi/2, -pi/2) $1;\ncz $0, $1;\nprx(pi/2, pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 4, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "qubit_names": { + "0": "QB1", + "1": "QB2" + }, + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "prx-0", + "polls": 1 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "vendor_counts", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "circuits", + "shots" + ], + "settings": {} + }, + "verbatim": false, + "note": "this client sends only circuits and shots: it sets nothing, so every step is the service's default, whatever that is", + "mitigation": "default", + "twirling": "default", + "decoupling": "default", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "49760803009a485dcaa4daa8fded38c14691d6f0629f112be48b76b4a0ef5cf0", + "signature": "7ab75ed5e9cf48a3daebacf508ec7dd69915a51b5a23200f1fdb91e7fc4b7480958ce39fcf8ae9a688ee5acaa771e818b6412f70e8ddd48441b70c2f10a2dd0c" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/scripted-sampler-v2.json b/wai/quantum-conformance/receipts/jobs/scripted-sampler-v2.json new file mode 100644 index 000000000..2fed418eb --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/scripted-sampler-v2.json @@ -0,0 +1,117 @@ +{ + "status": "done", + "method": "provider", + "why": "on mine, sampler-v2:test_device (sampler-v2): compiled exactly to rzsxxcz on its qubits and checked by simulation here before it was sent", + "request": { + "qasm": "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n", + "shots": 400, + "provider": "mine" + }, + "result": { + "method": "provider", + "qubits": 2, + "gates": 2, + "accuracy": { + "class": "measured", + "note": "counts from the processor the provider names, read as counts_source says (its shot records, the provider's own counts, or counts rebuilt here from its probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution" + }, + "shots": 400, + "provider": { + "name": "mine", + "kind": "sampler-v2", + "protocol": "sampler-v2", + "device": "sampler-v2:test_device", + "provenance": "silicon", + "qubits": 6, + "basis": "rzsxxcz", + "describe": "f25d38ec41b6c4e16dc2d5d2649b2ca23cddacb67d3de1b06add9766b5e3b879" + }, + "calibration": null, + "compiled": { + "basis": "rzsxxcz", + "program": "b96b3d2e44387bac1dab89e43e29706788b0b6ed721f88d5c0f64be293c72df9", + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": "OPENQASM 3.0;\ninclude \"stdgates.inc\";\nbit[2] c;\nrz(pi/2) $0;\nsx $0;\nrz(pi/2) $1;\nsx $1;\ncz $0, $1;\nrz(pi) $1;\nsx $1;\nrz(pi/2) $0;\nrz(pi/2) $1;\nc[0] = measure $0;\nc[1] = measure $1;\n", + "native_gates": 9, + "two_qubit_gates": 1, + "swaps": 0, + "layout": [ + 0, + 1 + ], + "final_layout": [ + 0, + 1 + ], + "checked": "the compiled program's final state equals the circuit's, each logical qubit on the physical qubit it ends on, by the bit-exact simulator here; nothing has been sent", + "placement": { + "chosen": "the compiler's own layout, the fewest swaps: the description does not read as a device target (describe: native_gates lists the device's gates)" + } + }, + "job": { + "id": "job-0", + "polls": 2 + }, + "ideal": { + "computed": true, + "method": "exact", + "total_variation_distance": 0.0, + "hellinger_fidelity": 1.0, + "sampling_tvd_expected": 0.019947114020071634, + "ideal_outcomes": 2, + "note": "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent" + }, + "cost": { + "free": false, + "estimate": { + "unknown": "the provider gives no estimate" + }, + "charged": null, + "confirmed": true + }, + "energy": { + "unmetered": "the work ran at the provider; this host's meter would read only the wait" + }, + "counts_source": "shots", + "execution": { + "stated_by": "client", + "sent": { + "fields": [ + "backend", + "params", + "program_id" + ], + "settings": { + "/params/options/dynamical_decoupling/enable": false, + "/params/options/twirling/enable_gates": false, + "/params/options/twirling/enable_measure": false + } + }, + "verbatim": false, + "note": "dynamical decoupling and twirling of gates and measurements are sent off; the request sets nothing for mitigation, remapping or optimisation, so the service's defaults stand; the program is sent on physical qubits in the backend's own gates", + "mitigation": "default", + "twirling": "off", + "decoupling": "off", + "remapping": "default", + "optimisation": "default" + }, + "counts": { + "00": 200, + "11": 200 + }, + "distinct_outcomes": 2, + "omitted_shots": 0, + "engine": { + "name": "wai-quantum", + "version": "0.8.1" + }, + "schema": "wai.quantum.job-result/1", + "receipt": { + "alg": "ed25519", + "canon": "rfc8785", + "key": "197f6b23e16c8532c6abc838facd5ea789be0c76b2920334039bfa8b3d368d61", + "digest": "a286c8aa7d5149740339db4a3bf0242cdd3fca94485cdaae01f63e3eff9f5710", + "signature": "e7d369b7df2a587ba2a117e1fcb3a176a51e21119787d3bc83fa41dd856f16ad0ccf953d539a5458e12638ed87048dcabb0f98b908207c7ca28822404c9b8306" + } + } +} diff --git a/wai/quantum-conformance/receipts/jobs/service-run-stabilizer.json b/wai/quantum-conformance/receipts/jobs/service-run-stabilizer.json new file mode 100644 index 000000000..87ea07bf4 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/service-run-stabilizer.json @@ -0,0 +1 @@ +{"id":"99516496183210d4f36137b10994ec1a","status":"done","method":"stabilizer","why":"Clifford circuit: the stabilizer tableau of 2 qubits","estimate":{"memory_bytes":67121670,"work":412},"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n","shots":200,"observables":["Z0 Z1"]},"submitted_ms":1793907035338,"started_ms":1793907035349,"finished_ms":1793907035568,"peak_rss_bytes":704512,"result":{"accuracy":{"class":"exact"},"counts":{"00":107,"11":93},"distinct_outcomes":2,"elapsed_ms":0,"energy":{"attribution":"process","coverage":"whole","covers":"the CPU energy of the job's worker process, from its start to its exit, with the CPU energy system services billed to it","cpu_ms":29,"energy_counter_filtering":"Unknown","energy_provenance":"OnChipCounter","energy_uncertainty":{"relative_ppm":43,"window_us":33139},"excludes":["memory","the host's idle draw"],"joules_micro":6781,"source":"proc_pid_rusage ri_energy_nj + ri_billed_energy − ri_serviced_energy, read after the worker exited and before it was reaped"},"engine":{"name":"wai-quantum","version":"0.8.1"},"expectations":[{"coeff":1,"observable":"Z0 Z1","value":1}],"gates":2,"method":"stabilizer","omitted_shots":0,"outcome_dimension":1,"provenance":{"circuit":{"gates":2,"program":"bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a","qubits":2},"compilation":{"asked":false,"status":"not-applicable","why":"the job asked for no compile: the program ran as the reader read it, on the method's own gates"},"computer":{"accuracy":"exact","kind":"simulator","method":"stabilizer"},"execution":{"plan":{"memory_bytes":67121670,"method":"stabilizer","why":"Clifford circuit: the stabilizer tableau of 2 qubits","work":412},"seed":1,"shots":200},"schema":"wai.quantum.provenance/1","software":{"engine":"wai-quantum 0.8.1","host":"wai-quantum-service 0.2.2"}},"qubits":2,"schema":"wai.quantum.job-result/1","receipt":{"alg":"ed25519","canon":"rfc8785","key":"06092a831e0a22da5bbfe5264e6160f47dcbe887feef31acdd213b7e037ccbf4","digest":"9a2b10a06e51ff6a6778a6fc53e0583bf1147beda866f1a581eba4ad88f4009d","signature":"7bed99bed5300de4536679441a03b0620bb2e792b1080e190d64197ffe1f0ab86fa43e0dfdf4a3138df8dc864962200b8febde8038d552f9adaf3470a08efc0a"}},"digests":{"algorithm":"blake3","request":"3a939348edae2911825a0d8b8efe2fad5717f75c8055dfb47b9a2781f8e59b56","programs":["bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a"],"result":"c1b065874f8fff9ad767b58caf28385fe69a4fcc8a01586102f5ac72552954a3","receipt":"fe5f28655bbaf8c69a3724cb1d4a4cef752862596ebf24302e1e3283e2cfdfc2"}} diff --git a/wai/quantum-conformance/receipts/jobs/service-run.json b/wai/quantum-conformance/receipts/jobs/service-run.json new file mode 100644 index 000000000..6c1dcc789 --- /dev/null +++ b/wai/quantum-conformance/receipts/jobs/service-run.json @@ -0,0 +1 @@ +{"id":"5486d43ebdad02b087eb6775b9c6f437","status":"done","method":"stabilizer","why":"Clifford circuit: the stabilizer tableau of 2 qubits","estimate":{"memory_bytes":67121670,"work":408},"request":{"qasm":"OPENQASM 3.0;\ninclude \"stdgates.inc\";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n","shots":200,"seed":11},"submitted_ms":1793907034945,"started_ms":1793907034956,"finished_ms":1793907035178,"peak_rss_bytes":114688,"result":{"accuracy":{"class":"exact"},"counts":{"00":102,"11":98},"distinct_outcomes":2,"elapsed_ms":0,"energy":{"attribution":"process","coverage":"partial","covers":"the CPU energy of the job's worker process, from its start to its exit, with the CPU energy system services billed to it","cpu_ms":32,"energy_counter_filtering":"Unknown","energy_provenance":"OnChipCounter","energy_uncertainty":{"relative_ppm":49,"window_us":35549},"excludes":["memory","the host's idle draw","any part of the system services' work not yet billed to the worker, and their GPU work"],"joules_micro":5895,"source":"proc_pid_rusage ri_energy_nj + ri_billed_energy − ri_serviced_energy, read after the worker exited and before it was reaped"},"engine":{"name":"wai-quantum","version":"0.8.1"},"gates":2,"method":"stabilizer","omitted_shots":0,"outcome_dimension":1,"provenance":{"circuit":{"gates":2,"program":"bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a","qubits":2},"compilation":{"asked":false,"status":"not-applicable","why":"the job asked for no compile: the program ran as the reader read it, on the method's own gates"},"computer":{"accuracy":"exact","kind":"simulator","method":"stabilizer"},"execution":{"plan":{"memory_bytes":67121670,"method":"stabilizer","why":"Clifford circuit: the stabilizer tableau of 2 qubits","work":408},"seed":11,"shots":200},"schema":"wai.quantum.provenance/1","software":{"engine":"wai-quantum 0.8.1","host":"wai-quantum-service 0.2.2"}},"qubits":2,"schema":"wai.quantum.job-result/1","receipt":{"alg":"ed25519","canon":"rfc8785","key":"06092a831e0a22da5bbfe5264e6160f47dcbe887feef31acdd213b7e037ccbf4","digest":"50eb9d1164e14d23069cc2f8f0ca4020987991a7cf97bcc0777916cae96c4585","signature":"bd1807c2b4646f5e1d3ef7f1d064ac40810418a68a3015de3a652eeee63b6b5853b0208c3d5ac646e7bcb758ba5ba69328fe97b63af66237b5afb2a89e790107"}},"digests":{"algorithm":"blake3","request":"30bb50f91a615b484fd74653f9c3c724cf028b8ee4169f216c99f3dab7e2b35b","programs":["bdcc1355a42c922e8b75eb5c015b12bc06b021de24042ab83ebca5df70d7dd8a"],"result":"9598d411a1bb0e6d09311167977c338406624fd50c2f7f3948d1d24fa660195d","receipt":"9c930f3fedf95d260a89ee051039a6bd412ed04602d0ce6821f698ef3015c5f4"}} diff --git a/wai/quantum-conformance/receipts/made.lock.json b/wai/quantum-conformance/receipts/made.lock.json new file mode 100644 index 000000000..54e1afcd7 --- /dev/null +++ b/wai/quantum-conformance/receipts/made.lock.json @@ -0,0 +1,25 @@ +{ + "cli-compile.json": "1afbddc31dd54eb98a28df16b472769b5e6b12915d8cc72eb5d0093cd8c29593", + "cli-provider-reference-priced.json": "e569e1810da66580c9da4c4fb1185490919d03066f104de8332f99ac214a728b", + "cli-provider-reference.json": "ade872d1a9e34a70f9f3928218b64b91ef2df8c21229778d79cd9cbbbb436f0c", + "cli-run-mps.json": "9f8fdd7f57566b63e66e979769caae330a32fe01520d909a82f1ecba5c5a6f04", + "cli-run-observables.json": "99c231f16c32f3b19fcf0734c51539d8bf152225999de10ae556462c6bdb0a53", + "cli-run-statevector.json": "31cd49c19c60c92932c98b228ead1e4d61bd207e9eea7c43fa0ffa976e188c2f", + "cli-run-trajectories.json": "c41c35bb5384d3582d83450f7b3d38225be2d353e4e3ad71e49a82c306656ede", + "cli-set-programs.json": "81059f216b449468ac4ab6803de72a0a5f894458cb178eae15f2abf42bfd2437", + "cli-set-sweep.json": "2b9af76474074a522ac5404c2de07a2b307620ce56e413b2319523b26fd168e1", + "first-form-service-observables.json": "098a720239f1757bb7ae7ceb8e35f7e0c30c91bb5fa7c977aef9aec02b855fa9", + "first-form-service-run.json": "f21a23fd33df0ec4c540bbc341c4296aeb4612bc4b9e58044604cbd19521a535", + "fixture-compile-legacy.json": "04c53ef2b350a4cdbd5f42deb8c44a91a1616a05c498a30f2f9cecd9c7402bca", + "fixture-job-legacy.json": "ee622c1cac908981aea75b51458a68dbe5f85b3d6f39b722fbc558412acb5e8d", + "fixture-provider-job-legacy.json": "2fb87ed2d0ba1ab99375dfb9ef8de07a16294414573a358e6d45413c6da408ab", + "scripted-cloud-tasks-processor-probabilities.json": "63f457f817d940d8af7e161b66aeb6b089e46c80a06c8b640b0a8ad3bd4c46cb", + "scripted-cloud-tasks-processor.json": "1eb499584183085209fa4597e0f8956826f18cf29d0885e7ad9a1c55a656f66d", + "scripted-cloud-tasks-simulator.json": "cc99f5d91138569f7584f913c88293a8581085ef6a38944f7425ad86591252c4", + "scripted-native-turns-noise.json": "8a6c1bac37f1b6a856d891dbc657d91c252a40eb7a5a09ab0ec9474233425385", + "scripted-native-turns.json": "9cc3b887aba07437b2a3ad6e20e7f656853c63bf8c94c21c715efdfddf95ee77", + "scripted-prx-circuits.json": "d1c74db10b01a23a7b66d90356a33b8f7afc37efd7c75a5af26fbd87f99cda6b", + "scripted-sampler-v2.json": "d19efb5521a9ff8114e68abea297211aa72f43bbe927025c210cd835c795e5eb", + "service-run-stabilizer.json": "295ff4f333dd728868beb6905e8081606eb04d7fa0333e3647f6de3b18ef7725", + "service-run.json": "c47a6a346800f56683b5bb67e3bc8991c50d7d591a58ee1a051fa96b6437eee3" +} diff --git a/wai/quantum-conformance/receipts/make.sh b/wai/quantum-conformance/receipts/make.sh new file mode 100644 index 000000000..50c14673b --- /dev/null +++ b/wai/quantum-conformance/receipts/make.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# make.sh: the receipts of the corpus that the command line makes (README.md). +# +# bash make.sh PATH/wai-quantum OUT_DIR +# +# Each is made as a person makes one: a run on this machine, a run on the reference +# provider served over a local process, and a run on the free job service (which needs +# the network). The runs here are sealed with the published demo key (--demo-key), so +# anyone can make the same; the service seals with its own. A receipt made again differs +# in its times and, on the service, its key, and verifies all the same: the corpus keeps +# the ones it was given, and never makes them again to pass a test. +set -euo pipefail +B=$(cd "$(dirname "$1")" && pwd -P)/$(basename "$1") +OUT=$(mkdir -p "$2" && cd "$2" && pwd -P) +W=$(mktemp -d) +trap 'rm -rf "$W"' EXIT +cd "$W" +export HOME="$W" XDG_CONFIG_HOME="$W/config" WAI_QUANTUM_CREDENTIALS=file +unset WAI_QUANTUM_KEY WAI_QUANTUM_PROVIDERS +printf 'OPENQASM 3.0;\ninclude "stdgates.inc";\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n' > bell.qasm +printf 'OPENQASM 3.0;\ninclude "stdgates.inc";\nqubit[3] q;\nbit[3] c;\nh q[0];\nt q[0];\ncx q[0], q[1];\nrz(pi/8) q[2];\ncx q[1], q[2];\nc = measure q;\n' > three.qasm +{ printf 'OPENQASM 3.0;\ninclude "stdgates.inc";\nqubit[30] q;\nbit[30] c;\n'; for i in $(seq 0 28); do printf 'rx(0.3) q[%d];\ncx q[%d], q[%d];\n' "$i" "$i" "$((i + 1))"; done; printf 'c = measure q;\n'; } > wide.qasm +printf 'OPENQASM 3.0;\ninclude "stdgates.inc";\nqubit[1] q;\nrx(0.4) q[0];\n' > rx.qasm +"$B" run bell.qasm --shots 1000 --seed 7 -o "Z0 Z1" -o X0 --demo-key --json > "$OUT/cli-run-observables.json" +"$B" run three.qasm --method statevector --shots 500 --seed 3 --demo-key --json > "$OUT/cli-run-statevector.json" +"$B" run bell.qasm --method trajectories --noise depolarizing_2q=0.03 --noise readout=0.02 --shots 300 --seed 5 -o "Z0 Z1" --demo-key --json > "$OUT/cli-run-trajectories.json" +"$B" run wide.qasm --method mps --shots 20 --seed 1 --demo-key --json > "$OUT/cli-run-mps.json" +"$B" compile rx.qasm --demo-key --json > "$OUT/cli-compile.json" +"$B" providers add ref --kind process -- "$B" providers reference --stdio > /dev/null +"$B" run bell.qasm --provider ref --shots 300 --seed 4 --demo-key --json > "$OUT/cli-provider-reference.json" +"$B" providers add priced --kind process -- "$B" providers reference --stdio --price 0.0003 --currency USD > /dev/null +"$B" run bell.qasm --provider priced --shots 300 --seed 4 --yes --demo-key --json > "$OUT/cli-provider-reference-priced.json" +printf 'OPENQASM 3.0;\ninclude "stdgates.inc";\ninput float theta;\nqubit[1] q;\nbit[1] c;\nrx(theta) q[0];\nc = measure q;\n' > theta.qasm +"$B" run bell.qasm --program three.qasm --shots 64 --seed 3 --demo-key --json > "$OUT/cli-set-programs.json" +"$B" run theta.qasm --sweep theta=0.25,0.5,1.5 --shots 64 --seed 9 --demo-key --json > "$OUT/cli-set-sweep.json" +"$B" submit bell.qasm --shots 200 --seed 11 --json > "$OUT/service-run.json" +"$B" submit bell.qasm --shots 200 -o "Z0 Z1" --json > "$OUT/service-run-stabilizer.json" +for f in "$OUT"/cli-*.json "$OUT"/service-*.json; do "$B" verify "$f" > /dev/null || { echo "make.sh: $f does not verify" >&2; exit 1; }; done +echo "make.sh: made $(ls "$OUT"/cli-*.json "$OUT"/service-*.json | wc -l) receipts in $OUT" diff --git a/wai/quantum-conformance/receipts/manifest.json b/wai/quantum-conformance/receipts/manifest.json new file mode 100644 index 000000000..3749608c8 --- /dev/null +++ b/wai/quantum-conformance/receipts/manifest.json @@ -0,0 +1,1167 @@ +{ + "about": "Job receipts and the reasons a verifier must give for each (wai/extensions/quantum-providers.md section 8.4): valid when there are none. made: sealed by the command line, the job service, the provider clients or an earlier engine, kept as made. crafted: made by generate.py from those, each changed in one way and sealed again with the published demo key where the change is inside the seal. Every expectation is written by hand from section 8.4.", + "entries": [ + { + "file": "jobs/cli-compile.json", + "reasons": [], + "made_by": "made", + "what": "the command line: a compile, its certificate and its receipt" + }, + { + "file": "jobs/cli-provider-reference-priced.json", + "reasons": [], + "made_by": "made", + "what": "the command line, on the reference provider with a price, confirmed with --yes" + }, + { + "file": "jobs/cli-provider-reference.json", + "reasons": [], + "made_by": "made", + "what": "the command line, on the reference provider over a local process: a simulator's stated result" + }, + { + "file": "jobs/cli-run-mps.json", + "reasons": [], + "made_by": "made", + "what": "the command line: thirty qubits as a matrix product state" + }, + { + "file": "jobs/cli-run-observables.json", + "reasons": [], + "made_by": "made", + "what": "the command line, on this machine, with --demo-key: a Bell pair's expectation values" + }, + { + "file": "jobs/cli-run-statevector.json", + "reasons": [], + "made_by": "made", + "what": "the command line: a three-qubit circuit's counts by the state vector" + }, + { + "file": "jobs/cli-run-trajectories.json", + "reasons": [], + "made_by": "made", + "what": "the command line: noisy trajectories, an estimate with its error" + }, + { + "file": "jobs/cli-set-programs.json", + "reasons": [], + "made_by": "made", + "what": "the command line: a set of two programs, one receipt over both and each its own (section 8.6)" + }, + { + "file": "jobs/cli-set-sweep.json", + "reasons": [], + "made_by": "made", + "what": "the command line: a sweep of an input at three points, sealed as a set" + }, + { + "file": "jobs/first-form-service-observables.json", + "reasons": [], + "made_by": "made", + "what": "the same service, the same writer: expectation values, doubles in its own text" + }, + { + "file": "jobs/first-form-service-run.json", + "reasons": [], + "made_by": "made", + "what": "the job service as it was on 18 October 2026, commit 72d18742d, sealing with the first form's own writer: counts" + }, + { + "file": "jobs/fixture-compile-legacy.json", + "reasons": [], + "made_by": "made", + "what": "a compile as an earlier page sealed it (the engine's pinned fixture)" + }, + { + "file": "jobs/fixture-job-legacy.json", + "reasons": [], + "made_by": "made", + "what": "a run sealed before results named their engine and schema (the engine's pinned fixture)" + }, + { + "file": "jobs/fixture-provider-job-legacy.json", + "reasons": [], + "made_by": "made", + "what": "a provider job of the earlier form: a simulator's counts sealed as measured, read as simulated" + }, + { + "file": "jobs/scripted-cloud-tasks-processor-probabilities.json", + "reasons": [], + "made_by": "made", + "what": "cloud-tasks on a processor whose result holds probabilities: counts rebuilt" + }, + { + "file": "jobs/scripted-cloud-tasks-processor.json", + "reasons": [], + "made_by": "made", + "what": "cloud-tasks on a processor: a verbatim box, rows of shots" + }, + { + "file": "jobs/scripted-cloud-tasks-simulator.json", + "reasons": [], + "made_by": "made", + "what": "cloud-tasks on its simulator: simulated" + }, + { + "file": "jobs/scripted-native-turns-noise.json", + "reasons": [], + "made_by": "made", + "what": "native-turns, its simulator under a device's noise model" + }, + { + "file": "jobs/scripted-native-turns.json", + "reasons": [], + "made_by": "made", + "what": "native-turns, its simulator, through a service that follows its contract" + }, + { + "file": "jobs/scripted-prx-circuits.json", + "reasons": [], + "made_by": "made", + "what": "prx-circuits through a service that follows its contract" + }, + { + "file": "jobs/scripted-sampler-v2.json", + "reasons": [], + "made_by": "made", + "what": "sampler-v2 through a service that follows its contract" + }, + { + "file": "jobs/service-run-stabilizer.json", + "reasons": [], + "made_by": "made", + "what": "the free job service: a stabilizer job with observables" + }, + { + "file": "jobs/service-run.json", + "reasons": [], + "made_by": "made", + "what": "the free job service, signed with its key" + }, + { + "file": "jobs/crafted-seal-alg-other.json", + "reasons": [ + "receipt_form" + ], + "made_by": "crafted", + "what": "an alg other than ed25519" + }, + { + "file": "jobs/crafted-seal-canon-null.json", + "reasons": [ + "receipt_form" + ], + "made_by": "crafted", + "what": "canon present and null: no form" + }, + { + "file": "jobs/crafted-seal-canon-other.json", + "reasons": [ + "receipt_form" + ], + "made_by": "crafted", + "what": "canon naming no form" + }, + { + "file": "jobs/crafted-seal-key-upper.json", + "reasons": [ + "receipt_form" + ], + "made_by": "crafted", + "what": "the key in upper-case hex: a second spelling of one key" + }, + { + "file": "jobs/crafted-seal-digest-plus.json", + "reasons": [ + "receipt_form" + ], + "made_by": "crafted", + "what": "a + where a hex digit belongs, which a lenient hex reader takes" + }, + { + "file": "jobs/crafted-seal-key-two-byte-char.json", + "reasons": [ + "receipt_form" + ], + "made_by": "crafted", + "what": "a two-byte character among the key's hex digits" + }, + { + "file": "jobs/crafted-seal-signature-short.json", + "reasons": [ + "receipt_form" + ], + "made_by": "crafted", + "what": "a signature one byte short" + }, + { + "file": "jobs/crafted-seal-key-missing.json", + "reasons": [ + "receipt_form" + ], + "made_by": "crafted", + "what": "no key" + }, + { + "file": "jobs/crafted-seal-receipt-string.json", + "reasons": [ + "receipt_form" + ], + "made_by": "crafted", + "what": "a receipt that is not an object" + }, + { + "file": "jobs/crafted-seal-weak-key-forgery.json", + "reasons": [ + "bad_key" + ], + "made_by": "crafted", + "what": "the identity as the key and as R, S = 0: a library's lenient check takes it for any digest, so any content verifies" + }, + { + "file": "jobs/crafted-seal-weak-key-any-digest.json", + "reasons": [ + "bad_key" + ], + "made_by": "crafted", + "what": "the identity as the key, R = [1]B, S = 1: [S]B = R + [k]A for every digest, R of full order" + }, + { + "file": "jobs/crafted-seal-key-noncanonical.json", + "reasons": [ + "bad_key" + ], + "made_by": "crafted", + "what": "a key whose y is p + 1: the identity, spelt a second way" + }, + { + "file": "jobs/crafted-seal-key-off-curve.json", + "reasons": [ + "bad_key" + ], + "made_by": "crafted", + "what": "a key that is no point on the curve" + }, + { + "file": "jobs/crafted-seal-key-mixed-order.json", + "reasons": [ + "bad_key" + ], + "made_by": "crafted", + "what": "the demo key plus a point of order 8: not of small order, outside the prime-order subgroup" + }, + { + "file": "jobs/crafted-seal-s-plus-l.json", + "reasons": [ + "bad_signature" + ], + "made_by": "crafted", + "what": "S + l: a second spelling of the same signature" + }, + { + "file": "jobs/crafted-seal-r-noncanonical.json", + "reasons": [ + "bad_signature" + ], + "made_by": "crafted", + "what": "R spelt with y of p or more" + }, + { + "file": "jobs/crafted-seal-r-small-order.json", + "reasons": [ + "bad_signature" + ], + "made_by": "crafted", + "what": "R the identity, S made so that the equation holds: a lax check takes it" + }, + { + "file": "jobs/crafted-seal-signature-other-key.json", + "reasons": [ + "bad_signature" + ], + "made_by": "crafted", + "what": "a signature by another key" + }, + { + "file": "jobs/crafted-seal-digest-stale.json", + "reasons": [ + "digest_mismatch" + ], + "made_by": "crafted", + "what": "the result changed after it was sealed: the digest alone" + }, + { + "file": "jobs/crafted-seal-digest-other.json", + "reasons": [ + "bad_signature", + "digest_mismatch" + ], + "made_by": "crafted", + "what": "a digest of something else, the signature over the first" + }, + { + "file": "jobs/crafted-form-first.json", + "reasons": [], + "made_by": "crafted", + "what": "a receipt of the first form, sealed over the compact text in the document's order" + }, + { + "file": "jobs/crafted-form-first-exponents.json", + "reasons": [], + "made_by": "crafted", + "what": "a first-form receipt with numbers its writer wrote with an exponent, signed both ways (1e+16, 1e-7)" + }, + { + "file": "jobs/crafted-form-first-big-integer.json", + "reasons": [], + "made_by": "crafted", + "what": "a first-form receipt sealing 2^53 + 1 by its digits, as its writer wrote it: not inexact" + }, + { + "file": "jobs/crafted-form-first-past-its-integers.json", + "reasons": [ + "inexact_number" + ], + "made_by": "crafted", + "what": "a first-form receipt with 2^64 + 1 written: past the integers its writer kept by their digits, it sealed the double 2^64" + }, + { + "file": "jobs/crafted-form-first-past-its-integers-exact.json", + "reasons": [], + "made_by": "crafted", + "what": "the same with 2^64 written, which the double holds" + }, + { + "file": "jobs/crafted-form-none-inexact.json", + "reasons": [ + "inexact_number", + "receipt_form" + ], + "made_by": "crafted", + "what": "a receipt with no canon and another alg names no form, so it is not of the first form, and 2^53 + 1 is inexact" + }, + { + "file": "jobs/crafted-form-first-relabelled.json", + "reasons": [ + "digest_mismatch" + ], + "made_by": "crafted", + "what": "a first-form receipt relabelled as canonical" + }, + { + "file": "jobs/crafted-form-canonical-relabelled.json", + "reasons": [ + "digest_mismatch" + ], + "made_by": "crafted", + "what": "a canonical receipt relabelled as the first form" + }, + { + "file": "jobs/crafted-form-first-reordered.json", + "reasons": [ + "digest_mismatch" + ], + "made_by": "crafted", + "what": "a first-form receipt with its result's members reordered" + }, + { + "file": "jobs/crafted-form-first-respelt.json", + "reasons": [ + "digest_mismatch" + ], + "made_by": "crafted", + "what": "a first-form receipt with 1.0 written 1, which its writer wrote 1.0" + }, + { + "file": "jobs/crafted-canon-respelt.json", + "reasons": [], + "made_by": "crafted", + "what": "members reordered, numbers written otherwise (1.0e3, 1), names and strings with escapes" + }, + { + "file": "jobs/crafted-canon-strings.json", + "reasons": [], + "made_by": "crafted", + "what": "strings and names that RFC 8785 writes its own way: UTF-16 order, escapes, characters left as they are" + }, + { + "file": "jobs/crafted-canon-numbers.json", + "reasons": [], + "made_by": "crafted", + "what": "numbers at the edges of ECMAScript's layout, and 2^64, a whole number a double holds" + }, + { + "file": "jobs/crafted-canon-underflow.json", + "reasons": [], + "made_by": "crafted", + "what": "1e-400 reads as zero, as sealed" + }, + { + "file": "jobs/crafted-canon-near-max.json", + "reasons": [], + "made_by": "crafted", + "what": "a number just below the limit, which rounds to the largest double" + }, + { + "file": "jobs/crafted-number-inexact.json", + "reasons": [ + "inexact_number" + ], + "made_by": "crafted", + "what": "2^53 + 1 written, 2^53 sealed: the text says more than the seal" + }, + { + "file": "jobs/crafted-number-inexact-unsealed.json", + "reasons": [], + "made_by": "crafted", + "what": "2^53 + 1 in a member of the receipt, which nothing seals and a verifier ignores" + }, + { + "file": "jobs/crafted-number-inexact-past-u64.json", + "reasons": [ + "inexact_number" + ], + "made_by": "crafted", + "what": "2^64 + 1 in the request, 2^64 sealed" + }, + { + "file": "jobs/crafted-read-duplicate.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "a member of the result named twice" + }, + { + "file": "jobs/crafted-read-duplicate-unsealed.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "a member of the unsealed envelope named twice: still unreadable" + }, + { + "file": "jobs/crafted-read-duplicate-escaped.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "two names that are one once their escapes are read" + }, + { + "file": "jobs/crafted-read-lone-surrogate.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "an escaped high surrogate with no low one" + }, + { + "file": "jobs/crafted-read-lone-low-surrogate.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "an escaped low surrogate alone" + }, + { + "file": "jobs/crafted-read-byte-order-mark.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "a byte order mark" + }, + { + "file": "jobs/crafted-read-raw-control.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "a tab not escaped inside a string" + }, + { + "file": "jobs/crafted-read-number-overflow.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "a number no double reaches, in the unsealed envelope" + }, + { + "file": "jobs/crafted-read-number-at-limit.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "2^1024 - 2^970 exactly, which rounds to infinity" + }, + { + "file": "jobs/crafted-read-depth-127.json", + "reasons": [], + "made_by": "crafted", + "what": "arrays and objects 127 deep, the most a reader takes" + }, + { + "file": "jobs/crafted-read-depth-128.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "128 deep" + }, + { + "file": "jobs/crafted-read-trailing.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "a second value after the first" + }, + { + "file": "jobs/crafted-read-not-json.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "not JSON" + }, + { + "file": "jobs/crafted-read-not-utf8.json", + "reasons": [ + "unreadable" + ], + "made_by": "crafted", + "what": "a byte that is not UTF-8" + }, + { + "file": "jobs/crafted-job-array.json", + "reasons": [ + "not_a_job" + ], + "made_by": "crafted", + "what": "a value that is not an object" + }, + { + "file": "jobs/crafted-job-no-request.json", + "reasons": [ + "not_a_job" + ], + "made_by": "crafted", + "what": "no request" + }, + { + "file": "jobs/crafted-job-request-string.json", + "reasons": [ + "not_a_job" + ], + "made_by": "crafted", + "what": "a request that is not an object" + }, + { + "file": "jobs/crafted-job-no-receipt.json", + "reasons": [ + "no_receipt" + ], + "made_by": "crafted", + "what": "a result with no receipt" + }, + { + "file": "jobs/crafted-envelope-changed.json", + "reasons": [], + "made_by": "crafted", + "what": "the unsealed envelope changed: nothing a verifier takes" + }, + { + "file": "jobs/crafted-provider-measured-simulator.json", + "reasons": [ + "provider_class" + ], + "made_by": "crafted", + "what": "a simulator's stated result labelled measured" + }, + { + "file": "jobs/crafted-provider-approximate-false.json", + "reasons": [ + "provider_class" + ], + "made_by": "crafted", + "what": "approximate given and not true" + }, + { + "file": "jobs/crafted-provider-approximate-processor.json", + "reasons": [ + "provider_class" + ], + "made_by": "crafted", + "what": "a processor's counts labelled approximate" + }, + { + "file": "jobs/crafted-provider-provenance-unknown.json", + "reasons": [ + "provider_class", + "provider_members" + ], + "made_by": "crafted", + "what": "a provenance that is none of silicon, simulator or emulator" + }, + { + "file": "jobs/crafted-provider-approximate-simulator.json", + "reasons": [], + "made_by": "crafted", + "what": "a simulator's result its provider states is approximate" + }, + { + "file": "jobs/crafted-provider-request-provider.json", + "reasons": [ + "request_provider" + ], + "made_by": "crafted", + "what": "the request names another provider" + }, + { + "file": "jobs/crafted-provider-request-shots.json", + "reasons": [ + "request_shots" + ], + "made_by": "crafted", + "what": "the request asks for other shots" + }, + { + "file": "jobs/crafted-provider-request-shots-default.json", + "reasons": [ + "request_shots" + ], + "made_by": "crafted", + "what": "the request gives no shots (1024), the result others" + }, + { + "file": "jobs/crafted-provider-counts-sum.json", + "reasons": [ + "counts" + ], + "made_by": "crafted", + "what": "counts that do not sum to the shots" + }, + { + "file": "jobs/crafted-provider-counts-width.json", + "reasons": [ + "counts" + ], + "made_by": "crafted", + "what": "outcomes of two lengths" + }, + { + "file": "jobs/crafted-provider-counts-zero.json", + "reasons": [ + "counts" + ], + "made_by": "crafted", + "what": "an outcome listed with a count of 0" + }, + { + "file": "jobs/crafted-provider-whole-past-safe.json", + "reasons": [], + "made_by": "crafted", + "what": "2^53 polls: a whole number has no ceiling but a count's" + }, + { + "file": "jobs/crafted-provider-whole-spelt-large.json", + "reasons": [], + "made_by": "crafted", + "what": "1e30 qubits: whole, and absurd, and no rule's to refuse" + }, + { + "file": "jobs/crafted-provider-shots-past-safe.json", + "reasons": [], + "made_by": "crafted", + "what": "2^53 shots, counted within a count's ceiling" + }, + { + "file": "jobs/crafted-provider-count-past-safe.json", + "reasons": [ + "counts" + ], + "made_by": "crafted", + "what": "a count of 2^53, past its ceiling" + }, + { + "file": "jobs/crafted-provider-shots-same-strings.json", + "reasons": [ + "counts" + ], + "made_by": "crafted", + "what": "equal strings are the request's shots, and a string is no shots" + }, + { + "file": "jobs/crafted-provider-counts-hidden.json", + "reasons": [ + "counts" + ], + "made_by": "crafted", + "what": "shots left out while fewer than 65,536 outcomes are listed" + }, + { + "file": "jobs/crafted-provider-counts-distinct.json", + "reasons": [ + "counts" + ], + "made_by": "crafted", + "what": "distinct_outcomes not the outcomes listed" + }, + { + "file": "jobs/crafted-provider-members-describe.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "the describe hash in upper case" + }, + { + "file": "jobs/crafted-provider-members-no-qasm3.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "no compiled program's text" + }, + { + "file": "jobs/crafted-provider-members-energy.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "energy as a figure measured here" + }, + { + "file": "jobs/crafted-provider-members-kwh-unlabelled.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "the provider's energy figure not labelled as its own" + }, + { + "file": "jobs/crafted-provider-members-no-schema.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "no schema" + }, + { + "file": "jobs/crafted-provider-members-no-charged.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "no charged" + }, + { + "file": "jobs/crafted-provider-members-basis.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "the provider's basis not the program's" + }, + { + "file": "jobs/crafted-provider-counts-source-other.json", + "reasons": [ + "counts_source" + ], + "made_by": "crafted", + "what": "a counts_source of no value" + }, + { + "file": "jobs/crafted-provider-execution-default.json", + "reasons": [ + "execution" + ], + "made_by": "crafted", + "what": "a provider saying default, which only a client says" + }, + { + "file": "jobs/crafted-provider-execution-verbatim-remapped.json", + "reasons": [ + "execution" + ], + "made_by": "crafted", + "what": "run as written and remapped" + }, + { + "file": "jobs/crafted-provider-execution-sent.json", + "reasons": [ + "execution" + ], + "made_by": "crafted", + "what": "sent, which only a client states" + }, + { + "file": "jobs/crafted-provider-execution-member.json", + "reasons": [ + "execution" + ], + "made_by": "crafted", + "what": "a member execution does not have" + }, + { + "file": "jobs/crafted-provider-execution-nobody.json", + "reasons": [], + "made_by": "crafted", + "what": "a provider that states nothing: no one's word" + }, + { + "file": "jobs/crafted-provider-execution-client-open.json", + "reasons": [ + "execution" + ], + "made_by": "crafted", + "what": "the open protocol's client claiming to state what was set" + }, + { + "file": "jobs/crafted-provider-compiled-program.json", + "reasons": [ + "compiled_program" + ], + "made_by": "crafted", + "what": "a program's text that is not the text its hash names" + }, + { + "file": "jobs/crafted-provider-cost-free-confirmed.json", + "reasons": [ + "cost_confirmation" + ], + "made_by": "crafted", + "what": "a free run that claims a confirmation" + }, + { + "file": "jobs/crafted-provider-cost-unconfirmed.json", + "reasons": [ + "cost_confirmation" + ], + "made_by": "crafted", + "what": "a run that was not free, with no confirmation" + }, + { + "file": "jobs/crafted-provider-stated-by-a-null.json", + "reasons": [ + "counts_source", + "execution", + "provider_class", + "provider_members" + ], + "made_by": "crafted", + "what": "an earlier-form result given counts_source null: stated by its members, whatever their values" + }, + { + "file": "jobs/crafted-earlier-request-shots.json", + "reasons": [ + "request_shots" + ], + "made_by": "crafted", + "what": "an earlier-form result whose request asks other shots" + }, + { + "file": "jobs/crafted-earlier-approximate.json", + "reasons": [ + "provider_class" + ], + "made_by": "crafted", + "what": "approximate on an earlier-form result" + }, + { + "file": "jobs/crafted-earlier-simulated.json", + "reasons": [ + "provider_class" + ], + "made_by": "crafted", + "what": "the earlier form labelled simulated, which no earlier engine wrote" + }, + { + "file": "jobs/crafted-kind-sampler-twirling-default.json", + "reasons": [ + "execution" + ], + "made_by": "crafted", + "what": "sampler-v2 not stating the twirling it sends off" + }, + { + "file": "jobs/crafted-kind-prx-shots.json", + "reasons": [ + "counts_source" + ], + "made_by": "crafted", + "what": "prx-circuits claiming shot records it does not read" + }, + { + "file": "jobs/crafted-kind-prx-twirling-off.json", + "reasons": [ + "execution" + ], + "made_by": "crafted", + "what": "prx-circuits claiming a setting it does not send" + }, + { + "file": "jobs/crafted-kind-turns-shots.json", + "reasons": [ + "counts_source" + ], + "made_by": "crafted", + "what": "native-turns claiming shots: its counts are always rebuilt from probabilities" + }, + { + "file": "jobs/crafted-kind-tasks-vendor-counts.json", + "reasons": [ + "counts_source" + ], + "made_by": "crafted", + "what": "cloud-tasks claiming the provider's own counts" + }, + { + "file": "jobs/crafted-kind-tasks-simulator-boxed.json", + "reasons": [ + "execution" + ], + "made_by": "crafted", + "what": "a simulator's task stating the processor's verbatim box" + }, + { + "file": "jobs/crafted-kind-tasks-no-names.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "cloud-tasks, stated by its client, not saying which of the device's qubits its indices were" + }, + { + "file": "jobs/crafted-kind-prx-no-names.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "prx-circuits, stated by its client, not saying which of the device's qubits its indices were" + }, + { + "file": "jobs/crafted-kind-tasks-names-null.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "null is given, and is no object" + }, + { + "file": "jobs/crafted-kind-tasks-name-leading-zero.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "an index with a leading zero" + }, + { + "file": "jobs/crafted-kind-sampler-names-short.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "names given where they are optional, and not of every index of the layouts" + }, + { + "file": "jobs/crafted-kind-prx-nobody.json", + "reasons": [], + "made_by": "crafted", + "what": "a job recorded before its client said what it sent: stated by nobody, on a kind with rows" + }, + { + "file": "jobs/crafted-kind-tasks-processor-unboxed.json", + "reasons": [ + "execution" + ], + "made_by": "crafted", + "what": "a processor's task not stating its verbatim box" + }, + { + "file": "jobs/crafted-kind-tasks-names-short-of-text.json", + "reasons": [ + "provider_members" + ], + "made_by": "crafted", + "what": "qasm3 acts on a qubit its names do not name, outside both layouts" + }, + { + "file": "jobs/crafted-number-canonical-spelling.json", + "reasons": [], + "made_by": "crafted", + "what": "2^60 written as RFC 8785 writes it, its shortest digits and zeros: the double's own spelling" + }, + { + "file": "jobs/crafted-number-exact-spelling.json", + "reasons": [], + "made_by": "crafted", + "what": "2^60 written by its exact digits" + }, + { + "file": "jobs/crafted-number-exponent-spelling.json", + "reasons": [], + "made_by": "crafted", + "what": "2^60 written with an exponent, as Python writes it" + }, + { + "file": "jobs/crafted-number-near-canonical.json", + "reasons": [ + "inexact_number" + ], + "made_by": "crafted", + "what": "digits one past the double's canonical text: another spelling of an inexact whole number" + }, + { + "file": "jobs/crafted-number-padded-past.json", + "reasons": [ + "inexact_number" + ], + "made_by": "crafted", + "what": "the digits padded past 10^21, where RFC 8785 writes an exponent: not its spelling" + }, + { + "file": "jobs/crafted-set-error-item.json", + "reasons": [], + "made_by": "crafted", + "what": "a set whose second item failed: its error is its leaf" + }, + { + "file": "jobs/crafted-set-item-changed.json", + "reasons": [ + "set_item" + ], + "made_by": "crafted", + "what": "an item's counts changed after it was sealed, the set sealed again over it" + }, + { + "file": "jobs/crafted-set-root-other.json", + "reasons": [ + "set_root" + ], + "made_by": "crafted", + "what": "a root that is no tree's of these items, sealed by the set" + }, + { + "file": "jobs/crafted-set-path-other.json", + "reasons": [ + "set_root" + ], + "made_by": "crafted", + "what": "an item's path changed: a path is not sealed, and must lead to the root" + }, + { + "file": "jobs/crafted-set-count-other.json", + "reasons": [ + "set_items" + ], + "made_by": "crafted", + "what": "a count other than the items'" + }, + { + "file": "jobs/crafted-set-index-swapped.json", + "reasons": [ + "set_items" + ], + "made_by": "crafted", + "what": "items whose index is not their place" + }, + { + "file": "jobs/crafted-set-item-not-given.json", + "reasons": [ + "set_items" + ], + "made_by": "crafted", + "what": "an item sealed alone, and not the item the request's programs give" + }, + { + "file": "jobs/crafted-set-head-changed.json", + "reasons": [ + "digest_mismatch" + ], + "made_by": "crafted", + "what": "the head changed after the set was sealed" + }, + { + "file": "jobs/crafted-set-error-item-inexact.json", + "reasons": [ + "set_item", + "set_items" + ], + "made_by": "crafted", + "what": "a failed item whose request writes a number its leaf seals as another: no item's number escapes" + }, + { + "file": "jobs/crafted-set-first-form-error-item.json", + "reasons": [], + "made_by": "crafted", + "what": "a set of the first form, an item failed: valid" + }, + { + "file": "jobs/crafted-set-first-form-error-item-inexact.json", + "reasons": [ + "set_item", + "set_items" + ], + "made_by": "crafted", + "what": "a set of the first form whose failed item writes 2^53 + 1 by its digits: its leaf, the canonical form's digest, seals the double" + }, + { + "file": "jobs/crafted-set-item-other-key.json", + "reasons": [ + "set_item" + ], + "made_by": "crafted", + "what": "an item sealed by a key other than the set's" + }, + { + "file": "jobs/crafted-set-empty.json", + "reasons": [ + "set_items" + ], + "made_by": "crafted", + "what": "a set of no items, its head sealed so" + }, + { + "file": "jobs/crafted-set-sweep-short.json", + "reasons": [ + "set_items" + ], + "made_by": "crafted", + "what": "a sweep missing its last point, counted and sealed so" + }, + { + "file": "jobs/crafted-set-sweep-inexact.json", + "reasons": [ + "inexact_number" + ], + "made_by": "crafted", + "what": "a sweep's value no double holds, in the set's own request" + }, + { + "file": "jobs/crafted-method-measured.json", + "reasons": [ + "provider_class" + ], + "made_by": "crafted", + "what": "the engine's own result labelled measured" + }, + { + "file": "jobs/crafted-method-simulated.json", + "reasons": [ + "provider_class" + ], + "made_by": "crafted", + "what": "the engine's own result labelled simulated" + }, + { + "file": "jobs/crafted-method-class-unknown.json", + "reasons": [], + "made_by": "crafted", + "what": "a class a verifier does not know: not a reason, since a later version may add one" + } + ] +} diff --git a/wai/quantum-conformance/tools/receipt_differential.py b/wai/quantum-conformance/tools/receipt_differential.py new file mode 100644 index 000000000..cb46ebd0e --- /dev/null +++ b/wai/quantum-conformance/tools/receipt_differential.py @@ -0,0 +1,334 @@ +#!/usr/bin/env python3 +"""receipt_differential.py: two verifiers of job receipts, held to each other and +to the corpus (wai/quantum-conformance/receipts/README.md). + + python3 -I receipt_differential.py --reference PATH/wai-quantum [--extra DIR ...] + python3 -I receipt_differential.py --reference PATH/wai-quantum --plant NAME + python3 -I receipt_differential.py --second-only [--plant NAME] + +The reference is `wai-quantum verify --json` (and `--lines`); the second verifier +is receipt_verify.py beside this file, written from +wai/extensions/quantum-providers.md section 8 alone. For every receipt of the +corpus, both must report exactly the reasons the manifest gives. Then every +valid receipt of the corpus is changed one member at a time, each member of +its request, its result, its receipt and its unsealed envelope, by removing it +and by replacing its value, and both must report the same reasons for every +change. Two oracles hold beside agreement: a change inside the request or the +result is never valid, and a change to the envelope, or a member added to the +receipt, never changes the verdict. + +`--extra DIR` adds receipts a person holds and does not publish (one with an +account number in it): both verifiers must find each valid. + +`--second-only` holds the second verifier to the manifest alone, with no build +of the reference, and changes nothing. + +`--plant NAME` runs the second verifier with one of its planted defects +(`receipt_verify.py --plant`), and succeeds only when the differential catches +it: the proof that the check can fail. + +Exit status 0 only when everything agrees (or, with --plant, when the plant is +caught). Standard library only, Python 3.9+. +""" +import argparse +import copy +import importlib.util +import json +import os +import subprocess +import sys +import tempfile +from collections import Counter + +HERE = os.path.dirname(os.path.abspath(__file__)) +CORPUS = os.path.join(HERE, "..", "receipts") +SECOND = os.path.join(HERE, "receipt_verify.py") + + +def compact(v): + return json.dumps(v, ensure_ascii=False, separators=(",", ":")) + + +def verdicts(lines_text, json_lines): + out = [] + for line in lines_text.splitlines(): + line = line.strip() + if line: + out.append(json.loads(line)) + if len(out) != json_lines: + raise SystemExit("a verifier gave %d verdicts for %d receipts" % (len(out), json_lines)) + return out + + +def run_reference_files(ref, files): + out = [] + for f in files: + p = subprocess.run([ref, "verify", "--json", f], capture_output=True, text=True) + if p.returncode not in (0, 1) or not p.stdout.strip(): + raise SystemExit("the reference failed on %s (status %d): %s" % (f, p.returncode, p.stderr.strip()[:400])) + out.append(json.loads(p.stdout.strip().splitlines()[-1])) + return out + + +def run_second_files(files, plant): + cmd = [sys.executable, "-I", SECOND] + (["--plant", plant] if plant else []) + files + p = subprocess.run(cmd, capture_output=True, text=True) + if p.returncode not in (0, 1): + raise SystemExit("the second verifier failed (status %d): %s" % (p.returncode, p.stderr.strip()[:400])) + return verdicts(p.stdout, len(files)) + + +def run_lines(cmd, path, n): + p = subprocess.run(cmd + [path], capture_output=True, text=True) + if p.returncode not in (0, 1): + raise SystemExit("%s failed (status %d): %s" % (cmd[0], p.returncode, p.stderr.strip()[:400])) + return verdicts(p.stdout, n) + + +def said(v): + """What a verdict says that both verifiers must agree on.""" + p = v.get("provider") or {} + return (tuple(sorted(v.get("reasons", []))), v.get("key"), v.get("form"), p.get("form"), p.get("read_as"), p.get("counts_source")) + + +# ----------------------------------------------------------------- one change at a time + +def mutations(body): + """(what, changed body, expected): each member removed, and each value + replaced; expected is `invalid` for a change inside the request, the result + or the receipt, and `unchanged` for one to the envelope or a member added + to the receipt, which is ignored.""" + out = [] + + def other(v): + if isinstance(v, bool): + return not v + if isinstance(v, int): + return v + 1 + if isinstance(v, float): + return v * 2 + 1 + if isinstance(v, str): + return v + "x" + if v is None: + return 0 + if isinstance(v, list): + return v + [copy.deepcopy(v[-1]) if v else 0] + added = dict(v) + added["z_added"] = 1 + return added + + def walk(v, path): + if isinstance(v, dict): + for k in list(v): + yield path + (k,), v[k], True + for x in walk(v[k], path + (k,)): + yield x + elif isinstance(v, list): + for i in sorted({0, len(v) - 1} if v else set()): + yield path + (i,), v[i], True + for x in walk(v[i], path + (i,)): + yield x + + def set_at(b, path, value=None, remove=False): + o = b + for k in path[:-1]: + o = o[k] + if remove: + del o[path[-1]] + else: + o[path[-1]] = value + + for path, v, removable in walk(body, ()): + envelope = path[0] not in ("request", "result") + name = "/".join(map(str, path)) + b = copy.deepcopy(body) + set_at(b, path, other(v)) + added_to_receipt = path == ("result", "receipt") and isinstance(v, dict) + # A set's item (section 8.6): its index, request, result, error and path are + # read; anything else it holds is the host's, unsealed, as an item added to + # is, and a member added to its own receipt is ignored as one added to a job's. + in_item = len(path) >= 3 and path[:2] == ("result", "items") and body["result"].get("format") == "wai.quantum.set/1" + item_extra = in_item and (len(path) == 3 and isinstance(v, dict) or len(path) >= 4 and path[3] not in ("index", "request", "result", "error", "path")) + item_receipt = in_item and len(path) >= 5 and path[3:5] == ("result", "receipt") and (len(path) == 5 and isinstance(v, dict) or len(path) >= 6 and path[5] not in ("alg", "canon", "key", "digest", "signature")) + # A member of the receipt beyond its five is ignored (section 8.2). + ignored = len(path) >= 3 and path[:2] == ("result", "receipt") and path[2] not in ("alg", "canon", "key", "digest", "signature") + out.append((name + " replaced", b, "unchanged" if envelope or added_to_receipt or ignored or item_extra or item_receipt else "invalid")) + if removable: + b = copy.deepcopy(body) + set_at(b, path, remove=True) + out.append((name + " removed", b, "unchanged" if envelope or ignored or (item_extra and len(path) >= 4) or (item_receipt and len(path) >= 6) else "invalid")) + return out + + +# Whole numbers past what a double holds, and others a verifier might cap or round. +EDGES = (2 ** 53, 2 ** 53 + 1, 1e16, 1e30, -1, -0.0, 0.5) +SHOTS = (2 ** 53, 1e16, "300", 300.0, 0, None) + + +def generator(): + """generate.py, for its seal: it holds the published demo seed.""" + spec = importlib.util.spec_from_file_location("receipts_generate", os.path.join(CORPUS, "generate.py")) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def edges(body, seal): + """(what, changed body, "agree"): a provider's result, each number set to each + of EDGES, and its shots in both its request and its result set to each of SHOTS. + There is no oracle here but the other verifier: the point is a limit one has and + the other does not. Those the demo key sealed are also sealed again, so that each + is judged by what it says and not only by its digest.""" + out = [] + if not (isinstance(body.get("result"), dict) and body["result"].get("method") == "provider"): + return out + + def numbers(v, path): + if isinstance(v, dict): + for k in v: + if not (path == ("result",) and k == "receipt"): + yield from numbers(v[k], path + (k,)) + elif isinstance(v, list): + for i in sorted({0, len(v) - 1} if v else set()): + yield from numbers(v[i], path + (i,)) + elif isinstance(v, (int, float)) and not isinstance(v, bool): + yield path + + demo = seal is not None and isinstance(body["result"].get("receipt"), dict) and body["result"]["receipt"].get("key") == seal[1] + + def both(name, b): + out.append((name, b, "agree")) + if demo: + out.append((name + ", sealed again", seal[0](b), "agree")) + + for path in numbers({"request": body.get("request"), "result": body["result"]}, ()): + for value in EDGES: + b = copy.deepcopy(body) + o = b + for k in path[:-1]: + o = o[k] + o[path[-1]] = value + both("/".join(map(str, path)) + " set to %r" % (value,), b) + for value in SHOTS: + b = copy.deepcopy(body) + b["request"]["shots"] = value + b["result"]["shots"] = value + both("the shots of both set to %r" % (value,), b) + return out + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--reference", help="the wai-quantum binary") + ap.add_argument("--second-only", action="store_true", help="the second verifier against the manifest alone") + ap.add_argument("--corpus", default=CORPUS) + ap.add_argument("--extra", action="append", default=[], help="a directory of receipts both must find valid") + ap.add_argument("--plant", help="run the second verifier with this planted defect; succeed only if it is caught") + a = ap.parse_args() + if not a.second_only and not a.reference: + ap.error("--reference is required, unless --second-only") + ref = os.path.abspath(a.reference) if a.reference else None + with open(os.path.join(a.corpus, "manifest.json"), encoding="utf-8") as f: + manifest = json.load(f)["entries"] + problems = [] + listed = {e["file"] for e in manifest} + present = {"jobs/" + n for n in os.listdir(os.path.join(a.corpus, "jobs")) if n.endswith(".json")} + for f in sorted(present - listed): + problems.append("%s is in the corpus and not in the manifest" % f) + for f in sorted(listed - present): + problems.append("%s is in the manifest and not in the corpus" % f) + + # The corpus: both verifiers and the manifest, receipt by receipt. + files = [os.path.join(a.corpus, e["file"]) for e in manifest if e["file"] in present] + entries = [e for e in manifest if e["file"] in present] + r2 = run_second_files(files, a.plant) + r1 = r2 if a.second_only else run_reference_files(ref, files) + agree = 0 + for e, x, y in zip(entries, r1, r2): + want = tuple(sorted(e["reasons"])) + if said(x) != said(y) or said(x)[0] != want: + problems.append("%s: the manifest says %s; the reference %s; the second verifier %s" % (e["file"], list(want), list(said(x)), list(said(y)))) + else: + agree += 1 + valid = sum(1 for e in entries if not e["reasons"]) + print("corpus: %d receipts (%d valid, %d not), %d agree %s" % (len(entries), valid, len(entries) - valid, agree, "with the manifest (second verifier only)" if a.second_only else "three ways (manifest, reference, second)")) + if a.second_only: + return finish(a, problems) + + # Receipts a person holds: both must find them valid. + extra = [] + for d in a.extra: + extra += [os.path.join(d, n) for n in sorted(os.listdir(d)) if n.endswith(".json")] + if extra: + x1, x2 = run_reference_files(ref, extra), run_second_files(extra, a.plant) + for f, x, y in zip(extra, x1, x2): + if said(x) != said(y) or said(x)[0]: + problems.append("%s (extra): the reference %s; the second verifier %s" % (os.path.basename(f), list(said(x)), list(said(y)))) + print("extra: %d receipts held outside the corpus, %d valid to both" % (len(extra), sum(1 for x, y in zip(x1, x2) if not said(x)[0] and said(x) == said(y)))) + + # Every valid receipt, one member changed at a time. + lines, meta = [], [] + g = generator() + seal = (g.seal, g.public_key(g.DEMO).hex()) + for e in entries: + if e["reasons"]: + continue + try: + with open(os.path.join(a.corpus, e["file"]), encoding="utf-8") as f: + body = json.load(f) + except ValueError: + continue + if not isinstance(body, dict): + continue + for what, b, expected in mutations(body) + edges(body, seal): + lines.append(compact(b)) + meta.append((e["file"], what, expected)) + with tempfile.TemporaryDirectory() as tmp: + path = os.path.join(tmp, "changed.jsonl") + with open(path, "w", encoding="utf-8") as f: + f.write("\n".join(lines) + "\n") + m1 = run_lines([ref, "verify", "--lines"], path, len(lines)) + m2 = run_lines([sys.executable, "-I", SECOND] + (["--plant", a.plant] if a.plant else []) + ["--lines"], path, len(lines)) + agree, caught, held, compared = 0, 0, 0, 0 + reasons = Counter() + for (fname, what, expected), x, y in zip(meta, m1, m2): + sx, sy = said(x), said(y) + if sx != sy: + problems.append("%s, %s: the reference %s; the second verifier %s" % (fname, what, list(sx), list(sy))) + continue + agree += 1 + reasons.update(sx[0]) + if expected == "invalid" and not sx[0]: + problems.append("%s, %s: a change inside the request, the result or the receipt was found valid by both" % (fname, what)) + elif expected == "unchanged" and sx[0]: + problems.append("%s, %s: a change that is not sealed changed the verdict: %s" % (fname, what, list(sx[0]))) + elif expected == "invalid": + caught += 1 + elif expected == "agree": + compared += 1 + else: + held += 1 + print("changed: %d receipts from %d valid ones; %d agree (%d refused, as every change inside the seal must be; %d valid, as every change outside it must be; %d at the edges of a number, judged only by the other verifier)" % (len(lines), valid, agree, caught, held, compared)) + print("reasons given to the changed receipts: " + ", ".join("%s %d" % kv for kv in sorted(reasons.items()))) + + return finish(a, problems) + + +def finish(a, problems): + if a.plant: + if problems: + print("plant %s: caught, by %d disagreements; the first: %s" % (a.plant, len(problems), problems[0])) + return 0 + print("plant %s: NOT caught: the differential cannot tell this defect from the spec" % a.plant) + return 1 + for p in problems[:60]: + print("DISAGREE " + p) + if problems: + print("%d disagreements" % len(problems)) + return 1 + print("receipt_differential: the two verifiers and the manifest agree on every receipt") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/wai/quantum-conformance/tools/receipt_verify.py b/wai/quantum-conformance/tools/receipt_verify.py new file mode 100644 index 000000000..e6810d532 --- /dev/null +++ b/wai/quantum-conformance/tools/receipt_verify.py @@ -0,0 +1,2063 @@ +#!/usr/bin/env python3 +"""receipt_verify.py: a second, independent verifier of WAI Quantum job receipts. + +Written from wai/extensions/quantum-providers.md §8 alone (the working text), and +sharing no code with the engine. Outside specifications it relies on: RFC 8785 +(JCS), RFC 8032 (Ed25519), the BLAKE3 specification, RFC 8259 (JSON), RFC 6901 +(JSON Pointer). Standard library only, Python 3.9 or later, run as `python3 -I`. + + python3 -I receipt_verify.py FILE... one verdict line per file + python3 -I receipt_verify.py --lines FILE one compact job a line, one verdict a line + python3 -I receipt_verify.py --self-test [ES_NUMBER_FILE] + python3 -I receipt_verify.py --plant NAME ... a deliberate fault (below), never for use + +A verdict line is + {"file", "valid", "reasons": [sorted §8.4 reasons], "key", "form"} +with "key" the receipt's key hex when the receipt is of its form (else null) and +"form" "rfc8785", "first" or null; a provider's result adds + "provider": {"form": "stated"|"earlier", "class", "read_as", "counts_source", "execution"}. +The exit status is 0 only when every verdict is valid. + +The reasons are exactly §8.4's and §8.6's: + unreadable, not_a_job, no_receipt (each reported alone); receipt_form, + digest_mismatch, bad_key, bad_signature; inexact_number; provider_class, + request_provider, request_shots, counts (every provider result); provider_members, + counts_source, execution, compiled_program, cost_confirmation (the stated form); + set_items, set_item, set_root (a set, §8.6). + +--plant NAME exists only to prove that a differential against another verifier +can fail: each plant breaks one rule on purpose, and a verifier run with one is +wrong by design. NAME is one of: + lenient-key accept small-order and mixed-order keys (a library's default) + no-alg skip the alg check + last-duplicate read a duplicated member name, keeping the last, not refusing + no-inexact skip inexact_number + measured-simulator accept class measured on a simulator's stated result + whole-cap cap every whole number at 2^53 − 1, not only a count's value + lax-small-order-r do not refuse an R of small order + no-canonical-spelling refuse an inexact whole number even in its canonical spelling + set-seal-only check a set's seal, and nothing of its items or its root + names-layout-only check compiled.qubit_names against the layouts only, not qasm3 +Without --plant the verifier is exactly §8's. + +Its reader's questions of the text, gap by gap, are what §8 now says: where §8 +left a choice, it was settled in the text, and this file follows the text. +""" + +import hashlib +import json +import math +import re +import struct +import sys +import time + +PLANTS_KNOWN = ("lenient-key", "no-alg", "last-duplicate", "no-inexact", "measured-simulator", + "whole-cap", "lax-small-order-r", "no-canonical-spelling", "set-seal-only", + "names-layout-only") +PLANTS = set() + +MAX_DEPTH = 127 # §8.2: nesting deeper than 127 is unreadable +MAX_SAFE = 2 ** 53 - 1 + + +# =============================================================== 1. reading (§8.2) + +class Unreadable(Exception): + """The text breaks a rule of reading (§8.2). `detail` is for the self-test only.""" + + def __init__(self, detail): + Exception.__init__(self, detail) + self.detail = detail + + +class Num(object): + """A JSON number: the text as written, and the double it reads as.""" + __slots__ = ("text", "value") + + def __init__(self, text, value): + self.text = text + self.value = value + + def literal_whole(self): + """§8.2: a whole number written as one (no fraction, no exponent).""" + return not any(c in self.text for c in ".eE") + + def __repr__(self): + return "Num(%s)" % self.text + + +_WS = re.compile(r"[ \t\n\r]*") +_NUMBER = re.compile(r"-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?") +_PLAIN = re.compile(r'[^"\\\x00-\x1f]*') +_HEX4 = re.compile(r"[0-9A-Fa-f]{4}") +_UNESCAPE = {'"': '"', "\\": "\\", "/": "/", "b": "\b", "f": "\f", + "n": "\n", "r": "\r", "t": "\t"} + + +def _number(text): + # §8.2: the nearest double, ties to even (Python's float() is correctly + # rounded); a magnitude of 2^1024 − 2^970 or more rounds to infinity and is + # unreadable; one too small for any double but zero reads as zero. + value = float(text) + if math.isinf(value): + raise Unreadable("number_overflow") + return Num(text, value) + + +class _Reader(object): + def __init__(self, text): + self.s = text + self.i = 0 + + def ws(self): + self.i = _WS.match(self.s, self.i).end() + + def value(self, depth): + self.ws() + s, i = self.s, self.i + c = s[i:i + 1] + if c == "{": + return self.obj(depth + 1) + if c == "[": + return self.arr(depth + 1) + if c == '"': + return self.string() + for word, val in (("true", True), ("false", False), ("null", None)): + if s.startswith(word, i): + self.i = i + len(word) + return val + m = _NUMBER.match(s, i) + if m: + self.i = m.end() + return _number(m.group()) + raise Unreadable("syntax") + + def obj(self, depth): + if depth > MAX_DEPTH: # the outermost counted as the first + raise Unreadable("depth") + self.i += 1 + out = {} + self.ws() + if self.s[self.i:self.i + 1] == "}": + self.i += 1 + return out + while True: + self.ws() + if self.s[self.i:self.i + 1] != '"': + raise Unreadable("syntax") + key = self.string() + self.ws() + if self.s[self.i:self.i + 1] != ":": + raise Unreadable("syntax") + self.i += 1 + value = self.value(depth) + # §8.2: a member named twice, names compared after their escapes are read. + if key in out: + if "last-duplicate" not in PLANTS: + raise Unreadable("duplicate") + del out[key] # PLANT: keep the last + out[key] = value + self.ws() + c = self.s[self.i:self.i + 1] + self.i += 1 + if c == "}": + return out + if c != ",": + raise Unreadable("syntax") + + def arr(self, depth): + if depth > MAX_DEPTH: + raise Unreadable("depth") + self.i += 1 + out = [] + self.ws() + if self.s[self.i:self.i + 1] == "]": + self.i += 1 + return out + while True: + out.append(self.value(depth)) + self.ws() + c = self.s[self.i:self.i + 1] + self.i += 1 + if c == "]": + return out + if c != ",": + raise Unreadable("syntax") + + def string(self): + s = self.s + i = self.i + 1 + parts = [] + while True: + m = _PLAIN.match(s, i) + parts.append(m.group()) + i = m.end() + c = s[i:i + 1] + if c == '"': + self.i = i + 1 + return "".join(parts) + if c != "\\": + # §8.2: an unescaped control character (or the text's end) + raise Unreadable("control" if c else "syntax") + e = s[i + 1:i + 2] + if e in _UNESCAPE: + parts.append(_UNESCAPE[e]) + i += 2 + continue + if e != "u" or not _HEX4.fullmatch(s, i + 2, i + 6): + raise Unreadable("syntax") + u = int(s[i + 2:i + 6], 16) + i += 6 + # §8.2: a high surrogate not followed at once by a low one, or a low alone. + if 0xD800 <= u <= 0xDBFF: + if s[i:i + 2] == "\\u" and _HEX4.fullmatch(s, i + 2, i + 6): + lo = int(s[i + 2:i + 6], 16) + if 0xDC00 <= lo <= 0xDFFF: + parts.append(chr(0x10000 + ((u - 0xD800) << 10) + (lo - 0xDC00))) + i += 6 + continue + raise Unreadable("surrogate") + if 0xDC00 <= u <= 0xDFFF: + raise Unreadable("surrogate") + parts.append(chr(u)) # §8.2: a noncharacter is a character like any other + + +def read_json(raw): + """§8.2: UTF-8, no byte order mark, one JSON value with whitespace around it.""" + if raw.startswith(b"\xef\xbb\xbf"): + raise Unreadable("bom") + try: + text = raw.decode("utf-8") # strict: no overlong forms, no encoded surrogates + except UnicodeDecodeError: + raise Unreadable("utf8") + reader = _Reader(text) + value = reader.value(0) + reader.ws() + if reader.i != len(text): + raise Unreadable("syntax") + return value + + +def _numbers(value): + stack = [value] + while stack: + v = stack.pop() + if isinstance(v, Num): + yield v + elif isinstance(v, dict): + stack.extend(v.values()) + elif isinstance(v, list): + stack.extend(v) + + +# ===================================================== 2. the two texts (§8.2) + +_ESCAPED = re.compile(r'[\x00-\x1f"\\]') +_SHORT_ESCAPE = {'"': '\\"', "\\": "\\\\", "\b": "\\b", "\f": "\\f", + "\n": "\\n", "\r": "\\r", "\t": "\\t"} + + +def _quote(s): + # §8.2: '"' and '\' escaped, the five short forms, other C0 controls as \u00xx + # (lower case), every other character as its UTF-8 bytes, '/' and U+007F included. + return '"' + _ESCAPED.sub( + lambda m: _SHORT_ESCAPE.get(m.group(), "\\u%04x" % ord(m.group())), s) + '"' + + +def _digits(x): + """For a finite x > 0: (s, n) with s the fewest digits that read back as x + (the nearest, a tie to the even last digit: Python's repr) and x = 0.s × 10^n.""" + mant, _, exp = repr(x).partition("e") + whole, _, frac = mant.partition(".") + raw = whole + frac + digits = raw.lstrip("0") + lead = len(raw) - len(digits) + return digits.rstrip("0"), len(whole) - lead + int(exp or 0) + + +def es_number(x): + """§8.2's canonical number: ECMAScript's Number::toString of the double.""" + if x == 0: + return "0" + sign = "-" if x < 0 else "" + d, n = _digits(abs(x)) + k = len(d) + if k <= n <= 21: + return sign + d + "0" * (n - k) + if 0 < n <= 21: + return sign + d[:n] + "." + d[n:] + if -6 < n <= 0: + return sign + "0." + "0" * (-n) + d + e = n - 1 + return sign + (d if k == 1 else d[0] + "." + d[1:]) + "e" + ("+" if e >= 0 else "-") + str(abs(e)) + + +def first_form_number(num): + """§8.2's first-form number.""" + t = num.text + # §8.2: without a "-" from 0 to 2^64 − 1, or with a "-" from −2^63 to −1, as + # its digits; so "-0" is a double: "-0.0". + if num.literal_whole() and t != "-0": + i = int(t) # at most 309 digits: longer ones are unreadable + if 0 <= i <= 2 ** 64 - 1 or -2 ** 63 <= i <= -1: + return str(i) + x = num.value + if x == 0: + return "-0.0" if math.copysign(1.0, x) < 0 else "0.0" + sign = "-" if x < 0 else "" + d, n = _digits(abs(x)) + k = len(d) + if k <= n <= 16: + return sign + d + "0" * (n - k) + ".0" + if 0 < n <= 16: + return sign + d[:n] + "." + d[n:] + if -5 < n <= 0: + return sign + "0." + "0" * (-n) + d + # §8.2: then e, a sign (+ or -), and |n − 1| (1e+16, 1e-7) + e = n - 1 + return sign + (d if k == 1 else d[0] + "." + d[1:]) + "e" + ("+" if e >= 0 else "-") + str(abs(e)) + + +def _utf16(s): + return s.encode("utf-16-be") + + +def text_of(value, canonical): + """§8.2: the canonical text, or (canonical False) the first form's compact text.""" + out = [] + _write(value, out, canonical) + return "".join(out) + + +def _write(v, out, canonical): + if v is None: + out.append("null") + elif v is True: + out.append("true") + elif v is False: + out.append("false") + elif isinstance(v, Num): + out.append(es_number(v.value) if canonical else first_form_number(v)) + elif isinstance(v, str): + out.append(_quote(v)) + elif isinstance(v, list): + out.append("[") + for j, x in enumerate(v): + if j: + out.append(",") + _write(x, out, canonical) + out.append("]") + else: + out.append("{") + # canonical: names in UTF-16 code-unit order; first form: as the job gives them + for j, k in enumerate(sorted(v, key=_utf16) if canonical else v): + if j: + out.append(",") + out.append(_quote(k)) + out.append(":") + _write(v[k], out, canonical) + out.append("}") + + +# ===================================================================== 3. BLAKE3 + +_IV = (0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19) +_PERM = (2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8) +_M32 = 0xFFFFFFFF +_CHUNK_START, _CHUNK_END, _PARENT, _ROOT = 1, 2, 4, 8 + + +def _g(v, a, b, c, d, x, y): + v[a] = (v[a] + v[b] + x) & _M32 + t = v[d] ^ v[a] + v[d] = ((t >> 16) | (t << 16)) & _M32 + v[c] = (v[c] + v[d]) & _M32 + t = v[b] ^ v[c] + v[b] = ((t >> 12) | (t << 20)) & _M32 + v[a] = (v[a] + v[b] + y) & _M32 + t = v[d] ^ v[a] + v[d] = ((t >> 8) | (t << 24)) & _M32 + v[c] = (v[c] + v[d]) & _M32 + t = v[b] ^ v[c] + v[b] = ((t >> 7) | (t << 25)) & _M32 + + +def _compress(cv, m, counter, block_len, flags): + v = list(cv) + list(_IV[:4]) + [counter & _M32, (counter >> 32) & _M32, block_len, flags] + m = list(m) + for _ in range(7): + _g(v, 0, 4, 8, 12, m[0], m[1]) + _g(v, 1, 5, 9, 13, m[2], m[3]) + _g(v, 2, 6, 10, 14, m[4], m[5]) + _g(v, 3, 7, 11, 15, m[6], m[7]) + _g(v, 0, 5, 10, 15, m[8], m[9]) + _g(v, 1, 6, 11, 12, m[10], m[11]) + _g(v, 2, 7, 8, 13, m[12], m[13]) + _g(v, 3, 4, 9, 14, m[14], m[15]) + m = [m[p] for p in _PERM] + return [v[i] ^ v[i + 8] for i in range(8)] + + +def _chunk_cv(chunk, counter, root): + cv = _IV + blocks = max(1, (len(chunk) + 63) // 64) + for b in range(blocks): + block = chunk[64 * b:64 * b + 64] + flags = _CHUNK_START if b == 0 else 0 + if b == blocks - 1: + flags |= _CHUNK_END | (_ROOT if root else 0) + cv = _compress(cv, struct.unpack("<16I", block.ljust(64, b"\0")), counter, len(block), flags) + return cv + + +def _parent_cv(left, right, root): + return _compress(_IV, left + right, 0, 64, _PARENT | (_ROOT if root else 0)) + + +def blake3(data): + """BLAKE3, hash mode, 32 bytes.""" + chunks = max(1, (len(data) + 1023) // 1024) + if chunks == 1: + return struct.pack("<8I", *_chunk_cv(data, 0, True)) + stack = [] + for i in range(chunks - 1): + cv = _chunk_cv(data[1024 * i:1024 * (i + 1)], i, False) + total = i + 1 + while total & 1 == 0: + cv = _parent_cv(stack.pop(), cv, False) + total >>= 1 + stack.append(cv) + cv = _chunk_cv(data[1024 * (chunks - 1):], chunks - 1, False) + while len(stack) > 1: + cv = _parent_cv(stack.pop(), cv, False) + return struct.pack("<8I", *_parent_cv(stack.pop(), cv, True)) + + +# ============================================================ 4. Ed25519 (§8.2) + +_P = 2 ** 255 - 19 +_L = 2 ** 252 + 27742317777372353535851937790883648493 +_D = -121665 * pow(121666, _P - 2, _P) % _P +_SQRT_M1 = pow(2, (_P - 1) // 4, _P) +_IDENTITY = (0, 1, 1, 0) + + +def _add(p, q): + x1, y1, z1, t1 = p + x2, y2, z2, t2 = q + a = (y1 - x1) * (y2 - x2) % _P + b = (y1 + x1) * (y2 + x2) % _P + c = 2 * t1 * t2 * _D % _P + d = 2 * z1 * z2 % _P + e, f, g, h = b - a, d - c, d + c, b + a + return (e * f % _P, g * h % _P, f * g % _P, e * h % _P) + + +def _mul(k, p): + q = _IDENTITY + while k: + if k & 1: + q = _add(q, p) + p = _add(p, p) + k >>= 1 + return q + + +def _same_point(p, q): + return ((p[0] * q[2] - q[0] * p[2]) % _P == 0 + and (p[1] * q[2] - q[1] * p[2]) % _P == 0) + + +def _decode_point(b): + """§8.2 (RFC 8032 §5.1.3): the point, or None for y ≥ p, off the curve, or x = 0 signed.""" + y = int.from_bytes(b, "little") + sign = y >> 255 + y &= (1 << 255) - 1 + if y >= _P: + return None + x2 = (y * y - 1) * pow(_D * y * y + 1, _P - 2, _P) % _P + x = pow(x2, (_P + 3) // 8, _P) + if (x * x - x2) % _P: + x = x * _SQRT_M1 % _P + if (x * x - x2) % _P: + return None + if x == 0 and sign: + return None + if x & 1 != sign: + x = _P - x + return (x, y, 1, x * y % _P) + + +def _encode_point(p): + zi = pow(p[2], _P - 2, _P) + x, y = p[0] * zi % _P, p[1] * zi % _P + return (y | ((x & 1) << 255)).to_bytes(32, "little") + + +def _small_order(p): + return _same_point(_mul(8, p), _IDENTITY) + + +_B = _decode_point((4 * pow(5, _P - 2, _P) % _P).to_bytes(32, "little")) + + +def key_point(key): + """§8.2: the key A as a point, or None when the strict rules refuse it (bad_key).""" + a = _decode_point(key) + if a is None: + return None + if "lenient-key" in PLANTS: # PLANT: a library's default verify + return a + if _small_order(a) or not _same_point(_mul(_L, a), _IDENTITY): + return None + return a + + +def signature_holds(a, key, digest, sig): + """§8.2: R decodes and is not of small order, S < ℓ, and [S]B = R + [k]A.""" + r = _decode_point(sig[:32]) + if r is None or ("lax-small-order-r" not in PLANTS and _small_order(r)): + return False + s = int.from_bytes(sig[32:], "little") + if s >= _L: + return False + k = int.from_bytes(hashlib.sha512(sig[:32] + key + digest).digest(), "little") % _L + return _same_point(_mul(s, _B), _add(r, _mul(k, a))) + + +def _ed25519_sign(seed, msg): + """RFC 8032 §5.1.6, for the self-test only: (public key, signature).""" + h = hashlib.sha512(seed).digest() + a = (int.from_bytes(h[:32], "little") & ((1 << 254) - 8)) | (1 << 254) + public = _encode_point(_mul(a, _B)) + r = int.from_bytes(hashlib.sha512(h[32:] + msg).digest(), "little") % _L + r_bytes = _encode_point(_mul(r, _B)) + k = int.from_bytes(hashlib.sha512(r_bytes + public + msg).digest(), "little") % _L + return public, r_bytes + ((r + k * a) % _L).to_bytes(32, "little") + + +# =============================================================== 5. the seal + +DOMAIN = b"wai:quantum-service-receipt" +_LOWER_HEX = re.compile(r"[0-9a-f]*") + + +def seal_digest(form_byte, q, t): + """§8.2: BLAKE3(d ‖ u64(|Q|) ‖ Q ‖ u64(|T|) ‖ T), d the domain and its form byte.""" + return blake3(DOMAIN + bytes([form_byte]) + struct.pack(">Q", len(q)) + q + + struct.pack(">Q", len(t)) + t) + + +def _lower_hex(v, length): + return isinstance(v, str) and len(v) == length and _LOWER_HEX.fullmatch(v) is not None + + +def check_seal(request, receipt, body, reasons): + """§8.4 receipt_form, digest_mismatch, bad_key, bad_signature, over the request and + the sealed body (a job's result less its receipt; a set's head). Returns (key, form).""" + form = None + if isinstance(receipt, dict): + if "canon" not in receipt: + form = "first" + elif receipt["canon"] == "rfc8785": + form = "rfc8785" + if (form is None + or ("no-alg" not in PLANTS and receipt.get("alg") != "ed25519") + or not _lower_hex(receipt.get("key"), 64) + or not _lower_hex(receipt.get("digest"), 64) + or not _lower_hex(receipt.get("signature"), 128)): + reasons.add("receipt_form") # and the next three are not checked + return None, None + key = bytes.fromhex(receipt["key"]) + stated = bytes.fromhex(receipt["digest"]) + sig = bytes.fromhex(receipt["signature"]) + + canonical = form == "rfc8785" + q = text_of(request, canonical).encode("utf-8") + t = text_of(body, canonical).encode("utf-8") + if stated != seal_digest(2 if canonical else 1, q, t): + reasons.add("digest_mismatch") + a = key_point(key) + if a is None: + reasons.add("bad_key") # and the signature is not checked + elif not signature_holds(a, key, stated, sig): + reasons.add("bad_signature") # over the digest the receipt states + return receipt["key"], form + + +def _first_form_digits(num): + """§8.2: a whole number the first form seals by its digits (its writer's integer range).""" + if not num.literal_whole() or num.text == "-0": + return False + i = int(num.text) + return 0 <= i <= 2 ** 64 - 1 or -2 ** 63 <= i <= -1 + + +def check_inexact(request, body, form, reasons): + # §8.4 inexact_number: a whole number written as one, in what the receipt seals + # (the request, and the result less its receipt; a set's request and head) and + # sealed as a double, is not a double, and is not the canonical text of the double + # it reads as. A receipt is of the first form when receipt_form is not reported and + # it has no canon; one receipt_form refuses names no form, and its numbers are + # read as a canonical receipt's (every number sealed as its double). + if any(writes_inexact(part, form) for part in (request, body)): + reasons.add("inexact_number") + + +def writes_inexact(value, form): + """§8.2, §8.4: whether a value writes a whole number, sealed as a double, that is no + double and not the canonical text of the double it reads as, by a receipt's form.""" + if "no-inexact" in PLANTS: # PLANT + return False + first = form == "first" + for n in _numbers(value): + if not n.literal_whole() or (first and _first_form_digits(n)): + continue + if int(n.text) == int(n.value): + continue # the double holds it exactly + # §8.2: its one taken spelling, RFC 8785's for a whole double whose magnitude is + # from 2^53 to under 10^21, a negative with its "-": shortest digits padded with + # zeros (2^60 as 1152921504606847000). + if "no-canonical-spelling" not in PLANTS and n.text == es_number(n.value): + continue + return True + return False + + +# ======================================================== 6. a provider's result + +STEPS = ("mitigation", "twirling", "decoupling", "remapping", "optimisation") +COUNTS_SOURCES = ("shots", "vendor_counts", "probabilities") +BASES = ("rzsxxcz", "prxcz", "gpizz") +MAX_LISTED = 65536 +_OUTCOME = re.compile(r"[01]*") +_INDEX = re.compile(r"0|[1-9][0-9]*") # §8.4: an index in decimal, with no leading zero +_QASM_INDEX = re.compile(r"\$([0-9]+)") # §8.4: a "$" and one or more decimal digits +_MODEL_NAME = object() # a row's placeholder: the noise model's name + +def _row(fields, settings, steps, verbatim): + row = {"stated_by": "client", "sent": {"fields": fields, "settings": settings}, + "verbatim": verbatim} + for step in STEPS: + row[step] = steps.get(step, "default") + return row + + +_CLOUD_TASKS_FIELDS = ["action", "clientToken", "deviceArn", "outputS3Bucket", + "outputS3KeyPrefix", "shots"] + +def _any_provenance(provenance): + return True + + +def _silicon(provenance): + return provenance == "silicon" + + +def _not_silicon(provenance): + return provenance != "silicon" + + +# §8.3: each built-in kind's rows as (the provenances it holds for, the counts' +# sources it gives, the execution less its note): cloud-tasks' first row for +# silicon, its second for any other; native-turns' row with noise for any but +# silicon (its model's name a string, not empty), its row without noise for any. +# §8.4: only a kind with rows constrains counts_source, to what the rows that hold +# for its provenance give; http, process and later kinds give any of the three. +_SHOTS, _VENDOR, _PROBABILITIES = ("shots",), ("vendor_counts",), ("probabilities",) +_TASKS = ("shots", "probabilities") +ROWS = { + "sampler-v2": [(_any_provenance, _SHOTS, _row( + ["backend", "params", "program_id"], + {"/params/options/dynamical_decoupling/enable": False, + "/params/options/twirling/enable_gates": False, + "/params/options/twirling/enable_measure": False}, + {"decoupling": "off", "twirling": "off"}, False))], + "prx-circuits": [(_any_provenance, _VENDOR, _row(["circuits", "shots"], {}, {}, False))], + "native-turns": [ + (_any_provenance, _PROBABILITIES, _row( + ["backend", "input", "settings", "shots", "type"], + {"/input/gateset": "native", "/settings/error_mitigation/debiasing": False}, + {"mitigation": "off"}, False)), + (_not_silicon, _PROBABILITIES, _row( + ["backend", "input", "noise", "settings", "shots", "type"], + {"/input/gateset": "native", "/settings/error_mitigation/debiasing": False, + "/noise/model": _MODEL_NAME}, + {"mitigation": "off"}, False))], + "cloud-tasks": [ + (_silicon, _TASKS, _row(_CLOUD_TASKS_FIELDS, {"verbatim_box": True}, + {"remapping": "off", "optimisation": "off"}, True)), + (_not_silicon, _TASKS, _row(_CLOUD_TASKS_FIELDS, {}, {}, False))], +} + + +def _whole(v): + """§8.4: a number whose value is whole, however it is written, however large + (only a count's value has a ceiling, and check_counts holds it).""" + if "whole-cap" in PLANTS and isinstance(v, Num) and abs(v.value) > MAX_SAFE: + return False # PLANT + return isinstance(v, Num) and v.value.is_integer() + + +def _same(a, b): + """JSON equality, numbers by value (spelling is not sealed).""" + if isinstance(a, Num) or isinstance(b, Num): + return isinstance(a, Num) and isinstance(b, Num) and a.value == b.value + if isinstance(a, bool) or isinstance(b, bool) or a is None or b is None: + return a is b + if isinstance(a, str) or isinstance(b, str): + return isinstance(a, str) and isinstance(b, str) and a == b + if isinstance(a, list) and isinstance(b, list): + return len(a) == len(b) and all(_same(x, y) for x, y in zip(a, b)) + if isinstance(a, dict) and isinstance(b, dict): + return set(a) == set(b) and all(_same(a[k], b[k]) for k in a) + return False + + +def read_class(provenance): + """§8.4: the class a provenance gives: measured, simulated, or None.""" + if provenance == "silicon": # §8.1: silicon exactly; "silicon:…" gives none + return "measured" + if isinstance(provenance, str) and provenance.split(":", 1)[0] in ("simulator", "emulator"): + return "simulated" + return None + + +def _member(obj, *path): + for name in path: + if not isinstance(obj, dict) or name not in obj: + return None + obj = obj[name] + return obj + + +def check_class(result, provider, stated, reasons): + # §8.4 provider_class. + accuracy = result.get("accuracy") + cls = accuracy.get("class") if isinstance(accuracy, dict) else None + if not provider: + if cls in ("measured", "simulated"): + reasons.add("provider_class") + return + wanted = read_class(_member(result, "provider", "provenance")) if stated else "measured" + planted = "measured-simulator" in PLANTS and stated and wanted == "simulated" and cls == "measured" + if (wanted is None or cls != wanted) and not planted: + reasons.add("provider_class") + if isinstance(accuracy, dict) and "approximate" in accuracy: + if accuracy["approximate"] is not True or not stated or cls != "simulated": + reasons.add("provider_class") + + +def check_counts(result, reasons): + # §8.4 counts. + counts = result.get("counts") + shots, omitted, distinct = (result.get(k) for k in ("shots", "omitted_shots", "distinct_outcomes")) + ok = isinstance(counts, dict) + total, widths = 0, set() + if ok: + for outcome, n in counts.items(): + if not _OUTCOME.fullmatch(outcome) or not (_whole(n) and 1 <= n.value <= MAX_SAFE): + ok = False + break + widths.add(len(outcome)) + total += int(n.value) + ok = ok and len(widths) <= 1 + # §8.4: shots a whole number of at least 1; omitted_shots and distinct_outcomes + # whole numbers of at least 0. + if (ok and _whole(shots) and shots.value >= 1 and _whole(omitted) and omitted.value >= 0 + and _whole(distinct) and distinct.value >= 0): + listed = len(counts) + omitted_n, distinct_n = int(omitted.value), int(distinct.value) + if total + omitted_n != int(shots.value): + ok = False + elif omitted_n == 0: + ok = distinct_n == listed + else: + ok = listed == MAX_LISTED and distinct_n > listed + else: + ok = False + if not ok: + reasons.add("counts") + + +def qubit_names_hold(names, compiled): + """§8.4: compiled.qubit_names as given: an object from indices to non-empty strings, + holding every entry of layout and final_layout, written in decimal in full, and + every index qasm3 acts on, when qasm3 is a string.""" + if not isinstance(names, dict): + return False + if not all(_INDEX.fullmatch(k) and isinstance(v, str) and v for k, v in names.items()): + return False + # SPEC-GAP: [N32] "in full": the exact digits of the double the entry reads as, + # so 1.0 is "1" and 1e23 is "99999999999999991611392". An entry that is no + # whole number is provider_members' already, and is not looked for here. + for name in ("layout", "final_layout"): + entries = compiled.get(name) + if isinstance(entries, list): + for q in entries: + if _whole(q) and str(int(q.value)) not in names: + return False + qasm3 = compiled.get("qasm3") + if "names-layout-only" not in PLANTS and isinstance(qasm3, str): # PLANT: skip it + # §8.4: each "$" followed by one or more decimal digits names the index those + # digits read as, written with no leading zero; a "$" with no digit names none. + # §8.4: a "$" anywhere in it, followed by the ASCII digits 0 to 9. + for m in _QASM_INDEX.finditer(qasm3): + if (m.group(1).lstrip("0") or "0") not in names: + return False + return True + + +def check_members(result, reasons): + # §8.4 provider_members: each member, present and of its type. + def text(v): + return isinstance(v, str) and v != "" + + def natural(v): # §8.4: a whole number of at least 0 + return _whole(v) and v.value >= 0 + + def naturals(v): + return isinstance(v, list) and all(natural(x) for x in v) + + p = result.get("provider") + c = result.get("compiled") + j = result.get("job") + cost = result.get("cost") + energy = result.get("energy") + engine = result.get("engine") + p = p if isinstance(p, dict) else {} + c = c if isinstance(c, dict) else {} + j = j if isinstance(j, dict) else {} + cost = cost if isinstance(cost, dict) else {} + energy = energy if isinstance(energy, dict) else {} + engine = engine if isinstance(engine, dict) else {} + charged = cost.get("charged", 0) + # §8.4: compiled.qubit_names is required of cloud-tasks and prx-circuits when the + # execution is stated by client, and optional otherwise; when given it holds its + # shape. SPEC-GAP: [N31] required on a cloud-tasks simulator too, as §8.4 says, + # though §8.1 names only "a cloud-tasks processor's labels". §8.4: null is + # given, and is no object. + names_needed = (p.get("kind") in ("cloud-tasks", "prx-circuits") + and _member(result, "execution", "stated_by") == "client") + checks = ( + all(text(p.get(f)) for f in ("name", "kind", "protocol", "device")), + read_class(p.get("provenance")) is not None, + _lower_hex(p.get("describe"), 64), + _whole(p.get("qubits")) and p["qubits"].value >= 1, + "basis" in p and "basis" in c and _same(p["basis"], c["basis"]), + "calibration" in result and (result["calibration"] is None or isinstance(result["calibration"], str)), + c.get("basis") in BASES if isinstance(c.get("basis"), str) else False, + _lower_hex(c.get("program"), 64), + isinstance(c.get("qasm3"), str), + all(natural(c.get(f)) for f in ("native_gates", "two_qubit_gates", "swaps")), + naturals(c.get("layout")) and naturals(c.get("final_layout")), + isinstance(c.get("checked"), str), + qubit_names_hold(c["qubit_names"], c) if "qubit_names" in c else not names_needed, + text(j.get("id")) and natural(j.get("polls")), + isinstance(_member(result, "ideal", "computed"), bool), + isinstance(cost.get("free"), bool) and isinstance(cost.get("confirmed"), bool), + isinstance(cost.get("estimate"), dict), # §8.1: with `unknown` when none was known + charged is None or (isinstance(charged, dict) and isinstance(charged.get("amount"), str) + and isinstance(charged.get("currency"), str)), + isinstance(energy.get("unmetered"), str), + "service_kwh" not in energy or isinstance(energy.get("service_kwh_is"), str), + result.get("schema") == "wai.quantum.job-result/1", + engine.get("name") == "wai-quantum" and isinstance(engine.get("version"), str), + ) + if not all(checks): + reasons.add("provider_members") + + +def execution_rules_hold(e): + """§8.3's rules of an execution.""" + if not isinstance(e, dict): + return False + if set(e) - set(STEPS + ("stated_by", "verbatim", "sent", "note")): + return False # "and nothing else" + if any(m not in e for m in STEPS + ("stated_by", "verbatim")): + return False + by = e["stated_by"] + # §8.3: client: off, on or default, verbatim true or false, sent given; + # provider: off, on, none or unknown, verbatim true or false, no sent; + # nobody: unknown, verbatim null, no sent. + if by == "client": + allowed, verbatim_ok, sent = ("off", "on", "default"), isinstance(e["verbatim"], bool), True + elif by == "provider": + allowed, verbatim_ok, sent = ("off", "on", "none", "unknown"), isinstance(e["verbatim"], bool), False + elif by == "nobody": + allowed, verbatim_ok, sent = ("unknown",), e["verbatim"] is None, False + else: + return False + if not verbatim_ok or ("sent" in e) != sent: + return False + if not all(isinstance(e[s], str) and e[s] in allowed for s in STEPS): + return False + if "note" in e and not isinstance(e["note"], str): + return False + if e["verbatim"] is True and not (e["remapping"] in ("off", "none") + and e["optimisation"] in ("off", "none")): + return False + if by != "client": + return True + s = e["sent"] + if not isinstance(s, dict) or set(s) != {"fields", "settings"}: + return False + fields, settings = s["fields"], s["settings"] + if not isinstance(fields, list) or not all(isinstance(f, str) for f in fields): + return False + encoded = [f.encode("utf-8") for f in fields] + if any(a >= b for a, b in zip(encoded, encoded[1:])): + return False # each once, in increasing order of their UTF-8 bytes + if not isinstance(settings, dict): + return False + # §8.3: each key of settings either begins with "/", and its first reference + # token (up to the next "/", its ~1 and ~0 read as "/" and "~") is one of the + # fields, the rest of it not read; or is a name, not empty, not beginning "/". + named = set(fields) # a list here would make this quadratic + for k in settings: + if not k.startswith("/"): + if not k: + return False + continue + first = k[1:].split("/", 1)[0].replace("~1", "/").replace("~0", "~") + if first not in named: + return False + if any(e[step] in ("off", "on") for step in STEPS) and not settings: + return False # a step said off or on says by which setting + return True + + +def _matches_row(e, row): + plain = dict((k, v) for k, v in e.items() if k != "note") + if set(plain) != set(row): + return False + for name, want in row.items(): + got = plain[name] + if name != "sent": + if not (got is want if isinstance(want, bool) else got == want): + return False + continue + if not (isinstance(got.get("fields"), list) and got["fields"] == want["fields"]): + return False + have = got.get("settings") + if not isinstance(have, dict) or set(have) != set(want["settings"]): + return False + for k, v in want["settings"].items(): + if v is _MODEL_NAME: + if not (isinstance(have[k], str) and have[k]): + return False + elif not (have[k] is v if isinstance(v, bool) else have[k] == v): + return False + return True + + +def check_stated(result, reasons): + """§8.4's rules for the stated form beyond provider_members.""" + kind = _member(result, "provider", "kind") + kind = kind if isinstance(kind, str) else None + + source = result.get("counts_source") + provenance = _member(result, "provider", "provenance") + if not (isinstance(source, str) and source in COUNTS_SOURCES + and (kind not in ROWS or any(holds(provenance) and source in sources + for holds, sources, _ in ROWS[kind]))): + reasons.add("counts_source") + + e = result.get("execution") + # §8.4: the rules of §8.3; for a kind with rows, one of the rows that hold for + # its provenance, or stated by nobody; for http and process, stated by provider + # or nobody. A later kind is held to §8.3's rules alone. + if not execution_rules_hold(e): + reasons.add("execution") + elif kind in ROWS and e["stated_by"] != "nobody" and not any( + holds(provenance) and _matches_row(e, row) for holds, _, row in ROWS[kind]): + reasons.add("execution") + elif kind in ("http", "process") and e["stated_by"] not in ("provider", "nobody"): + reasons.add("execution") + + # §8.4 compiled_program, when qasm3 is a string and program 64 lower-case hex digits. + c = result.get("compiled") + if (isinstance(c, dict) and isinstance(c.get("qasm3"), str) and _lower_hex(c.get("program"), 64) + and bytes.fromhex(c["program"]) != blake3(c["qasm3"].encode("utf-8"))): + reasons.add("compiled_program") + + # §8.4 cost_confirmation: confirmed true on a free run, or false on one that is not. + cost = result.get("cost") + if isinstance(cost, dict): + free, confirmed = cost.get("free"), cost.get("confirmed") + if isinstance(free, bool) and isinstance(confirmed, bool) and free is confirmed: + reasons.add("cost_confirmation") + + +def check_provider(request, result, reasons): + stated = "counts_source" in result or "execution" in result + check_class(result, True, stated, reasons) + # §8.4 request_provider: provider.name a string equal to the request's provider. + name, asked = _member(result, "provider", "name"), request.get("provider") + if not (isinstance(name, str) and isinstance(asked, str) and name == asked): + reasons.add("request_provider") + # §8.4 request_shots: the request's shots, 1024 when it gives none or null. + want = request.get("shots") + want = Num("1024", 1024.0) if want is None else want + if "shots" not in result or not _same(result["shots"], want): + reasons.add("request_shots") + check_counts(result, reasons) + if stated: + check_members(result, reasons) + check_stated(result, reasons) + + +def _plain(v): + """A value as Python's json module writes it, for the verdict line.""" + if isinstance(v, Num): + return int(v.value) if v.value.is_integer() and abs(v.value) < 2 ** 63 else v.value + if isinstance(v, dict): + return dict((k, _plain(x)) for k, x in v.items()) + if isinstance(v, list): + return [_plain(x) for x in v] + return v + + +def provider_statement(result): + """§8.4: for a provider's result, its form, its class as its provenance reads it, + and in the stated form its counts' source and its execution.""" + stated = "counts_source" in result or "execution" in result + accuracy = result.get("accuracy") + return { + "form": "stated" if stated else "earlier", + "class": _plain(accuracy.get("class")) if isinstance(accuracy, dict) else None, + "read_as": read_class(_member(result, "provider", "provenance")), + "counts_source": _plain(result.get("counts_source")) if stated else None, + "execution": _plain(result.get("execution")) if stated else None, + } + + +# ===================================================================== 7. a job + +def _verdict(reasons, key=None, form=None): + return {"valid": not reasons, "reasons": sorted(reasons), "key": key, "form": form} + + +def judge(raw): + """One job's text to its verdict (without "file").""" + try: + job = read_json(raw) + except Unreadable: + return _verdict(["unreadable"]) + if not (isinstance(job, dict) and isinstance(job.get("request"), dict) + and isinstance(job.get("result"), dict)): + return _verdict(["not_a_job"]) + # §8.2: only request and result are sealed; nothing else in the body is read. + request, result = job["request"], job["result"] + if result.get("format") == SET_FORMAT: + return judge_set(request, result) + return judge_job(request, result) + + +def judge_job(request, result): + """§8.4: a job's request and result, already read, to its verdict.""" + provider = result.get("method") == "provider" + if "receipt" not in result: + verdict = _verdict(["no_receipt"]) + else: + reasons = set() + body = dict((k, v) for k, v in result.items() if k != "receipt") + key, form = check_seal(request, result["receipt"], body, reasons) + check_inexact(request, body, form, reasons) + if provider: + check_provider(request, result, reasons) + else: + check_class(result, False, False, reasons) + verdict = _verdict(reasons, key, form) + if provider: + verdict["provider"] = provider_statement(result) + return verdict + + +# ===================================================================== 8. a set (§8.6) + +SET_FORMAT = "wai.quantum.set/1" +_MERKLE_LEAF = b"moq-jwp:merkle-leaf\x01" +_MERKLE_NODE = b"moq-jwp:merkle-node\x01" + + +def judge_set(request, result): + """§8.6: a set's request and result, already read, to its verdict. The seal is over + the request and the head (the result less items and receipt); the provider rules + do not apply to a head.""" + if "receipt" not in result: + return _verdict(["no_receipt"]) + reasons = set() + head = dict((k, v) for k, v in result.items() if k not in ("items", "receipt")) + key, form = check_seal(request, result["receipt"], head, reasons) + check_inexact(request, head, form, reasons) + if "set-seal-only" not in PLANTS: # PLANT: nothing of the items or the root + check_set(request, result, reasons) + return _verdict(reasons, key, form) + + +def _item_formed(i, item): + """§8.6 set_items, the first sentence: item i's own form. An item is read for its + index, request, result, error and path; anything else it holds is unsealed.""" + if not isinstance(item, dict): + return False + index = item.get("index") + if not (_whole(index) and index.value == i and isinstance(item.get("request"), dict)): + return False + # §8.6: exactly one of the members result and error present, whatever its value. + if ("result" in item) == ("error" in item): + return False + if "result" in item and not isinstance(item["result"], dict): + return False + if "error" in item and not isinstance(item["error"], str): + return False + path = item.get("path") + return isinstance(path, list) and all(_lower_hex(h, 64) for h in path) + + +def _items_given(request, items): + """§8.6: whether the items are the ones the request gives.""" + programs, sweep = request.get("programs"), request.get("sweep") # null is none + base = dict((k, v) for k, v in request.items() if k not in ("programs", "sweep", "dry_run")) + # SPEC-GAP: [N42] "neither" read as: neither a programs that is an array nor a + # sweep that is an object; "both" as: both members there and not null. A programs + # that is no array, or a sweep that is no object, gives no items. + if programs is not None and sweep is not None: + return not items + if isinstance(programs, list): + if len(programs) != len(items): + return False + for program, item in zip(programs, items): + wanted = dict(base) + if isinstance(program, str): + wanted["qasm"] = program + elif isinstance(program, dict): + wanted.update(program) # each member replacing one of the same name + else: + # SPEC-GAP: [N43] a program neither a string nor an object gives no item. + return False + if not _same(item["request"], wanted): + return False + return True + if isinstance(sweep, dict): + # SPEC-GAP: [N44] a sweep value that is no non-empty array: the items are not the + # ones the request gives, whatever they are. An empty sweep's product is 1. + product = 1 + for values in sweep.values(): + if not (isinstance(values, list) and values): + return False + product *= len(values) + if product > len(items): + return False + if product != len(items): + return False + # §8.6: each item's request less qasm is the request less programs, sweep, + # dry_run and qasm; its qasm a string (the program written out at its point, + # which §8 does not check). + base.pop("qasm", None) + for item in items: + asked = item["request"] + if not isinstance(asked.get("qasm"), str): + return False + if not _same(dict((k, v) for k, v in asked.items() if k != "qasm"), base): + return False + return True + return not items + + +def item_leaf(item): + """§8.6: an item's leaf, the canonical form's digest of its request and its result + less its receipt, or of its request and {"error": the error}.""" + q = text_of(item["request"], True).encode("utf-8") + if "result" in item: + body = dict((k, v) for k, v in item["result"].items() if k != "receipt") + else: + body = {"error": item["error"]} + return seal_digest(2, q, text_of(body, True).encode("utf-8")) + + +def _leaf_hash(leaf): + return blake3(_MERKLE_LEAF + leaf) + + +def _node_hash(left, right): + return blake3(_MERKLE_NODE + left + right) + + +def merkle_levels(hashes): + """Each level of the tree from the leaves' hashes up; a level of odd length pairs + its last hash with itself.""" + levels = [list(hashes)] + while len(levels[-1]) > 1: + level = levels[-1] + levels.append([_node_hash(level[j], level[j + 1] if j + 1 < len(level) else level[j]) + for j in range(0, len(level), 2)]) + return levels + + +def merkle_path(levels, position): + """The siblings from a leaf up (the self-test makes paths with it).""" + path = [] + for level in levels[:-1]: + sibling = position + 1 if position % 2 == 0 else position - 1 + path.append(level[sibling] if sibling < len(level) else level[position]) + position //= 2 + return path + + +def _halvings(n): + count = 0 + while n > 1: + n = (n + 1) // 2 + count += 1 + return count + + +def _root_holds(root, items): + """§8.6 set_root: root is the Merkle root of the leaves, and each path leads to it.""" + if not _lower_hex(root, 64) or not items: + return False # (no items is set_items', and not checked here) + hashes = [_leaf_hash(item_leaf(item)) for item in items] + want = bytes.fromhex(root) + if merkle_levels(hashes)[-1][0] != want: + return False + depth = _halvings(len(items)) + for position, (item, h) in enumerate(zip(items, hashes)): + # §8.6: a path of any other length does not lead to the root. + if len(item["path"]) != depth: + return False + at = position + for sibling in item["path"]: + other = bytes.fromhex(sibling) + h = _node_hash(h, other) if at % 2 == 0 else _node_hash(other, h) + at //= 2 + if h != want: + return False + return True + + +def check_set(request, result, reasons): + """§8.6 set_items, set_item, set_root.""" + items = result.get("items") + # set_items: a non-empty array of well-formed items; reported so, the next two + # are not checked. + if not (isinstance(items, list) and items + and all(_item_formed(i, it) for i, it in enumerate(items))): + reasons.add("set_items") + return + count = result.get("count") + if not (isinstance(count, Num) and count.value == len(items)) or not _items_given(request, items): + reasons.add("set_items") + if not all(_item_holds(item, result["receipt"]) for item in items): + reasons.add("set_item") + if not _root_holds(result.get("root"), items): + reasons.add("set_root") + + +def _item_holds(item, set_receipt): + """§8.6 set_item, for one item.""" + if "error" in item: + # §8.6: an item with an error: its request writes no whole number its leaf + # could not seal, as inexact_number reads one in a canonical receipt, whatever + # the set's form, since a leaf is the canonical form's digest. + return not writes_inexact(item["request"], "rfc8785") + item_result = item["result"] + if item_result.get("format") == SET_FORMAT: + return False # an item is one job + verdict = judge_job(item["request"], item_result) + if verdict["reasons"]: + return False + # §8.6: a valid item's receipt names the set's receipt's `key` member as it + # stands; one the set's receipt does not have is another. + set_key = set_receipt.get("key") if isinstance(set_receipt, dict) else None + return isinstance(set_key, str) and verdict["key"] == set_key + + +def verdict_for(name, raw): + try: + v = judge(raw) + except (RecursionError, MemoryError, Exception) as e: + # Never a traceback. This should not happen; §8.4 has no reason for it. + sys.stderr.write("receipt_verify: internal error on %s (%s): reported as unreadable\n" + % (name, type(e).__name__)) + v = {"valid": False, "reasons": ["unreadable"], "key": None, "form": None} + out = {"file": name} + out.update(v) + return out + + +def _read(path): + try: + with open(path, "rb") as f: + return f.read() + except (OSError, ValueError, MemoryError): + return None + + +def _usage(): + sys.stderr.write("usage: receipt_verify.py [--plant NAME]... (FILE... | --lines FILE)\n" + " receipt_verify.py --self-test [ES_NUMBER_FILE]\n") + return 2 + + +def main(argv): + if not argv or argv[0] in ("-h", "--help"): + return _usage() + if argv[0] == "--self-test": + return self_test(argv[1:]) + verdicts, ok, i = [], True, 0 + while i < len(argv): + arg = argv[i] + if arg in ("--plant", "--lines"): + if i + 1 >= len(argv): + return _usage() + value = argv[i + 1] + i += 2 + if arg == "--plant": + if value not in PLANTS_KNOWN: + return _usage() + PLANTS.add(value) + sys.stderr.write("receipt_verify: planted fault %s: verdicts are wrong by design\n" % value) + continue + raw = _read(value) + if raw is None: + verdicts.append({"file": value, "valid": False, "reasons": ["unreadable"], + "key": None, "form": None}) + continue + lines = raw.split(b"\n") + if lines and lines[-1] == b"": + lines.pop() + if not lines: + sys.stderr.write("receipt_verify: no job in %s\n" % value) + ok = False + for n, line in enumerate(lines, 1): + verdicts.append(verdict_for("%s:%d" % (value, n), line)) + else: + i += 1 + raw = _read(arg) + if raw is None: + verdicts.append({"file": arg, "valid": False, "reasons": ["unreadable"], + "key": None, "form": None}) + else: + verdicts.append(verdict_for(arg, raw)) + try: + for v in verdicts: + sys.stdout.write(json.dumps(v) + "\n") + sys.stdout.flush() + except (OSError, ValueError): + return 1 + return 0 if ok and all(v["valid"] for v in verdicts) else 1 + + +# ================================================================ 9. self-test + +_BLAKE3_VECTORS = ( # BLAKE3's test vectors: byte i is i mod 251 + (0, "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"), + (1, "2d3adedff11b61f14c886e35afa036736dcd87a74d27b5c1510225d0f592e213"), + (1023, "10108970eeda3eb932baac1428c7a2163b0e924c9a9e25b35bba72b28f70bd11"), + (1024, "42214739f095a406f3fc83deb889744ac00df831c10daa55189b5d121c855af7"), + (1025, "d00278ae47eb27b34faecf67b4fe263f82d5412916c1ffd97c8cb7fb814b8444"), + (2048, "e776b6028c7cd22a4d0ba182a8bf62205d2ef576467e838ed6f2529b85fba24a"), +) +_BLAKE3_ABC = "6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85" + +_RFC8032_VECTORS = ( # RFC 8032 §7.1 TEST 1, 2, 3 + ("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", + "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a", "", + "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"), + ("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb", + "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c", "72", + "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00"), + ("c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7", + "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025", "af82", + "6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a"), +) + +_SMALL_ORDER = ( # identity, order 2, the two of order 4 + "0100000000000000000000000000000000000000000000000000000000000000", + "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000080", +) + +_CANONICAL_VECTORS = ( # RFC 8785 §3.2.2, §3.2.3, and numbers + ('{"numbers":[333333333.33333329,1E30,4.50,2e-3,0.000000000000000000000000001],' + '"string":"\\u20ac$\\u000F\\u000aA\'\\u0042\\u0022\\u005c\\\\\\"\\/",' + '"literals":[null,true,false]}', + '{"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],' + '"string":"\u20ac$\\u000f\\nA\'B\\"\\\\\\\\\\"/"}'), + ('{"\\u20ac":1,"\\r":2,"\\ufb33":3,"1":4,"\\ud83d\\ude00":5,"\\u0080":6,"\\u00f6":7}', + '{"\\r":2,"1":4,"\u0080":6,"\u00f6":7,"\u20ac":1,"\U0001f600":5,"\ufb33":3}'), + ('[-0, 0.0, 1.0, 1e21, 1e-7, 123e-9, 5e-324, 1e-400, 1.7976931348623157e308]', + '[0,0,1,1e+21,1e-7,1.23e-7,5e-324,0,1.7976931348623157e+308]'), +) + +_FIRST_FORM_VECTORS = ( + ("[1.0,1,-0,0.0,1e16,1e15,0.00001,0.000001,1.5e300,18446744073709551615," + "18446744073709551616,-9223372036854775808,-9223372036854775809,2.5,-1.5e-7]", + "[1.0,1,-0.0,0.0,1e+16,1000000000000000.0,0.00001,1e-6,1.5e+300,18446744073709551615," + "1.8446744073709552e+19,-9223372036854775808,-9.223372036854776e+18,2.5,-1.5e-7]"), + # §8.2: a positive exponent with its sign, a negative one as before. + ("[1e16,1.5e300,1e-7,1e15]", "[1e+16,1.5e+300,1e-7,1000000000000000.0]"), +) + +_UNREADABLE_VECTORS = ( + (b'{"a":1,"a":2}', "duplicate"), + (b'{"a":1,"\\u0061":2}', "duplicate"), + (b'["\\ud800"]', "surrogate"), + (b'["\\ud800x"]', "surrogate"), + (b'["\\udc00\\ud800"]', "surrogate"), + (b'["a\x01"]', "control"), + (b'[1e400]', "number_overflow"), + (b'[-1e400]', "number_overflow"), + (str(2 ** 1024 - 2 ** 970).encode(), "number_overflow"), + (b'\xef\xbb\xbf{}', "bom"), + (b'["\xed\xa0\x80"]', "utf8"), + (b'["\xc0\xaf"]', "utf8"), + (b'{"a":1,}', "syntax"), + (b'[01]', "syntax"), + (b'[NaN]', "syntax"), + (b'{} {}', "syntax"), + (b'', "syntax"), + (b'[' * 128 + b']' * 128, "depth"), +) + +_READABLE_VECTORS = ( + b'[' * 127 + b']' * 127, + str(2 ** 1024 - 2 ** 970 - 1).encode(), + b'[1e-400, "\\ufffe", "\\ufdd0", "\xef\xbf\xbf"]', +) + + +def _seal(seed, request_text, result_text, form="rfc8785", receipt_extra=None, envelope=""): + """A job body sealed here, for the self-test only.""" + request, result = read_json(request_text.encode()), read_json(result_text.encode()) + canonical = form == "rfc8785" + digest = seal_digest(2 if canonical else 1, text_of(request, canonical).encode(), + text_of(result, canonical).encode()) + public, sig = _ed25519_sign(seed, digest) + receipt = {"alg": "ed25519", "key": public.hex(), "digest": digest.hex(), "signature": sig.hex()} + if canonical: + receipt["canon"] = "rfc8785" + receipt.update(receipt_extra or {}) + body = '{%s"request":%s,"result":%s,"receipt":%s}}' % ( + envelope, request_text, result_text[:-1], json.dumps(receipt, separators=(",", ":"))) + return body.encode() + + +_PROVIDER_REQUEST = '{"qasm":"OPENQASM 3.0;\\nqubit[1] q;\\nbit[1] c;\\nx q[0];\\nc = measure q;\\n","provider":"lab","shots":4}' +_PROVIDER_QASM3 = "OPENQASM 3.0;\nbit[1] c;\nx $0;\nc[0] = measure $0;\n" + + +def _provider_result(**changes): + r = { + "method": "provider", "qubits": 1, "gates": 1, + "accuracy": {"class": "simulated", "note": "counts a simulator returned"}, + "provider": {"name": "lab", "kind": "process", "protocol": "wai.quantum.provider/1", + "device": "reference", "provenance": "simulator:the reference simulator", + "qubits": 2, "basis": "rzsxxcz", "describe": "ab" * 32}, + "calibration": "snap-1", + "compiled": {"basis": "rzsxxcz", "qasm3": _PROVIDER_QASM3, + "program": blake3(_PROVIDER_QASM3.encode()).hex(), "hash": "BLAKE3 of qasm3", + "native_gates": 1, "two_qubit_gates": 0, "swaps": 0, "layout": [0], + "final_layout": [0], "checked": "checked here", "placement": {}}, + "job": {"id": "j-1", "polls": 0}, + "counts": {"1": 4}, "distinct_outcomes": 1, "omitted_shots": 0, "shots": 4, + "counts_source": "shots", + "execution": {"stated_by": "provider", "mitigation": "off", "twirling": "none", + "decoupling": "none", "remapping": "off", "optimisation": "off", + "verbatim": True}, + "ideal": {"computed": True, "total_variation_distance": 0, "hellinger_fidelity": 1}, + "cost": {"free": True, "estimate": {"free": True}, "charged": None, "confirmed": False}, + "energy": {"unmetered": "the work ran at the provider"}, + "engine": {"name": "wai-quantum", "version": "0.9.0"}, "schema": "wai.quantum.job-result/1", + } + for path, value in changes.items(): + node = r + parts = path.split("__") + for part in parts[:-1]: + node = node[part] + if value is _DELETE: + node.pop(parts[-1], None) + else: + node[parts[-1]] = value + return json.dumps(r, separators=(",", ":")) + + +_DELETE = object() + + +def _has_num(o): + if isinstance(o, Num): + return True + if isinstance(o, dict): + return any(_has_num(v) for v in o.values()) + if isinstance(o, list): + return any(_has_num(v) for v in o) + return False + + +def _dump(o): + """JSON text of a self-test value, a Num written by its own text.""" + if not _has_num(o): + return json.dumps(o) + return _first_text(o) + + +def _first_text(o): + if isinstance(o, Num): + return o.text + if isinstance(o, dict): + return "{" + ",".join(json.dumps(k) + ":" + _first_text(v) for k, v in o.items()) + "}" + if isinstance(o, list): + return "[" + ",".join(_first_text(v) for v in o) + "]" + return json.dumps(o) + + +def _seal_set(seed, request, head, items, receipt_extra=None, form="rfc8785"): + """A set sealed here over its request and head, for the self-test only.""" + canonical = form == "rfc8785" + drop_key, upper_key = head.pop("_drop_key", False), head.pop("_upper_key", False) + q = text_of(read_json(_dump(request).encode()), canonical).encode() + t = text_of(read_json(_dump(head).encode()), canonical).encode() + digest = seal_digest(2 if canonical else 1, q, t) + public, sig = _ed25519_sign(seed, digest) + result = dict(head, items=items) + receipt = {"alg": "ed25519", "key": public.hex(), "digest": digest.hex(), "signature": sig.hex()} + if canonical: + receipt["canon"] = "rfc8785" + result["receipt"] = dict(receipt, **(receipt_extra or {})) + if drop_key: + del result["receipt"]["key"] + if upper_key: + result["receipt"]["key"] = result["receipt"]["key"].upper() + return ("{\"request\":%s,\"result\":%s}" % (_dump(request), _dump(result))).encode() + + +_ITEM_RESULT = ('{"method":"statevector","qubits":1,"gates":1,"accuracy":{"class":"exact"},' + '"counts":{"1":10}}') + + +def _set_body(seed, request, item_requests, errors=(), before_root=None, after_root=None, + head_extra=None, item_result=_ITEM_RESULT, item_seed=None, form="rfc8785"): + """A set of items made and sealed here, each item a job sealed with the same seed. + before_root changes the items before the leaves are taken; after_root changes the + head and items after the root and paths are made, before the set is sealed.""" + items = [] + for i, asked in enumerate(item_requests): + if i in errors: + items.append({"index": i, "request": asked, "error": "it failed"}) + else: + job = json.loads(_seal(item_seed or seed, _dump(asked), item_result).decode()) + items.append({"index": i, "request": asked, "result": job["result"]}) + if before_root: + before_root(items) + hashes = [_leaf_hash(item_leaf(read_json(_dump(item).encode()))) for item in items] + levels = merkle_levels(hashes) if hashes else [[]] + for i, item in enumerate(items): + item["path"] = [h.hex() for h in merkle_path(levels, i)] + head = {"format": SET_FORMAT, "count": len(items), + "root": levels[-1][0].hex() if hashes else "00" * 32} + head.update(head_extra or {}) + if after_root: + after_root(head, items) + return _seal_set(seed, request, head, items, form=form) + + +def self_test(args): + results = [] + + def check(name, ok): + results.append((name, bool(ok))) + + def reasons_of(raw): + return judge(raw)["reasons"] + + seed = bytes(range(32)) + + # BLAKE3 + for n, want in _BLAKE3_VECTORS: + check("blake3 length %d" % n, blake3(bytes(i % 251 for i in range(n))).hex() == want) + check("blake3 abc", blake3(b"abc").hex() == _BLAKE3_ABC) + + # Ed25519, RFC 8032 §7.1, then §8.2's strict rules. + for sk, pk, msg, sig in _RFC8032_VECTORS: + pub, made = _ed25519_sign(bytes.fromhex(sk), bytes.fromhex(msg)) + a = key_point(bytes.fromhex(pk)) + check("rfc8032 %s public" % pk[:8], pub.hex() == pk) + check("rfc8032 %s sign" % pk[:8], made.hex() == sig) + check("rfc8032 %s verify" % pk[:8], a is not None and signature_holds( + a, bytes.fromhex(pk), bytes.fromhex(msg), bytes.fromhex(sig))) + key, msg, sig = (bytes.fromhex(x) for x in _RFC8032_VECTORS[1][1:]) + a = key_point(key) + s = int.from_bytes(sig[32:], "little") + check("S + l refused", not signature_holds(a, key, msg, sig[:32] + (s + _L).to_bytes(32, "little"))) + check("another message refused", not signature_holds(a, key, b"\x73", sig)) + for p in _SMALL_ORDER: + check("small-order key %s refused" % p[:6], key_point(bytes.fromhex(p)) is None) + check("non-canonical key refused", key_point((_P + 1).to_bytes(32, "little")) is None) + mixed = _encode_point(_add(a, _decode_point(bytes.fromhex(_SMALL_ORDER[1])))) + check("mixed-order key refused", key_point(mixed) is None) + + # §8.2 reading + for raw, detail in _UNREADABLE_VECTORS: + try: + read_json(raw) + got = None + except Unreadable as e: + got = e.detail + check("unreadable %r" % raw[:16], got == detail) + for raw in _READABLE_VECTORS: + try: + read_json(raw) + check("readable %r" % raw[:16], True) + except Unreadable: + check("readable %r" % raw[:16], False) + + # §8.2 texts + for text, want in _CANONICAL_VECTORS: + check("canonical %s" % text[:20], text_of(read_json(text.encode()), True) == want) + for text, want in _FIRST_FORM_VECTORS: + check("first-form numbers", text_of(read_json(text.encode()), False) == want) + if args: + lines = 0 + with open(args[0], "rb") as f: + for line in f.read().decode("ascii").splitlines(): + if line.strip(): + hexbits, want = line.split(" ", 1) + lines += 1 + if es_number(struct.unpack(">d", bytes.fromhex(hexbits))[0]) != want: + check("es number %s" % hexbits, False) + check("es number table, %d lines" % lines, lines > 0) + + # Seals of both forms, made here, and what breaks them (§8.4). + req = '{"qasm":"OPENQASM 3.0;\\nqubit[1] q;\\nbit[1] c;\\nx q[0];\\nc = measure q;\\n","shots":10}' + res = ('{"method":"statevector","qubits":1,"gates":1,"accuracy":{"class":"exact"},' + '"counts":{"1":10},"expectations":[{"observable":"Z0","coeff":1.0,"value":-1.0}]}') + for form in ("rfc8785", "first"): + body = _seal(seed, req, res, form) + v = judge(body) + check("%s seals" % form, v["reasons"] == [] and v["form"] == form and v["key"] is not None) + check("%s tampered: digest_mismatch alone" % form, + reasons_of(body.replace(b'"1":10', b'"1":11')) == ["digest_mismatch"]) + relabelled = (body.replace(b'"canon":"rfc8785",', b"").replace(b',"canon":"rfc8785"', b"") + if form == "rfc8785" else body.replace(b'"alg":"ed25519"', b'"alg":"ed25519","canon":"rfc8785"')) + check("%s relabelled" % form, reasons_of(relabelled) == ["digest_mismatch"]) + check("%s upper-case key" % form, reasons_of(body.replace( + v["key"].encode(), v["key"].upper().encode())) == ["receipt_form"]) + check("%s envelope not read" % form, reasons_of(body.replace( + b'{"request"', b'{"status":"failed","method":"other","id":7,"request"')) == []) + body = _seal(seed, req, res) + check("canon null names no form", reasons_of(body.replace(b'"canon":"rfc8785"', b'"canon":null')) == ["receipt_form"]) + check("alg other", reasons_of(body.replace(b'"alg":"ed25519"', b'"alg":"ed448"')) == ["receipt_form"]) + check("unknown receipt member ignored", reasons_of(body.replace(b'"alg":', b'"x":"y","alg":')) == []) + check("canonical survives re-spelling and order", reasons_of(body.replace( + b'"coeff":1.0', b'"coeff":1e0').replace(b'"qubits":1,"gates":1', b'"gates":1, "qubits":1')) == []) + first = _seal(seed, req, res, "first") + check("first form refuses member order", reasons_of(first.replace( + b'"qubits":1,"gates":1', b'"gates":1,"qubits":1')) == ["digest_mismatch"]) + check("first form refuses re-spelling", reasons_of(first.replace(b'"coeff":1.0', b'"coeff":1')) == ["digest_mismatch"]) + big = _seal(seed, req.replace('"shots":10', '"shots":10,"seed":9007199254740993'), res) + check("inexact_number", reasons_of(big) == ["inexact_number"]) + check("first form seals a u64 by its digits", reasons_of( + _seal(seed, req.replace('"shots":10', '"shots":10,"seed":9007199254740993'), res, "first")) == []) + check("first form past u64 is sealed as a double", reasons_of( + _seal(seed, req.replace('"shots":10', '"shots":10,"seed":18446744073709551617'), res, "first")) + == ["inexact_number"]) + check("first form 2^64 is a double, exactly", reasons_of( + _seal(seed, req.replace('"shots":10', '"shots":10,"seed":18446744073709551616'), res, "first")) == []) + check("first form below -2^63", reasons_of( + _seal(seed, req.replace('"shots":10', '"shots":10,"seed":-9223372036854775809'), res, "first")) + == ["inexact_number"]) + def seeded(seed_text, form="rfc8785"): + return _seal(seed, req.replace('"shots":10', '"shots":10,"seed":%s' % seed_text), res, form) + + check("2^60 in its canonical spelling", reasons_of(seeded("1152921504606847000")) == []) + check("-2^60 in its canonical spelling", reasons_of(seeded("-1152921504606847000")) == []) + check("2^60's double, in another spelling", reasons_of(seeded("1152921504606847001")) == ["inexact_number"]) + check("2^60 + 1, inexact", reasons_of(seeded("1152921504606846977")) == ["inexact_number"]) + check("2^53 + 1 is no double's canonical text", reasons_of(seeded("9007199254740993")) == ["inexact_number"]) + check("under 10^21, read as 10^21: no canonical whole spelling", reasons_of( + seeded("999999999999999999999")) == ["inexact_number"]) + check("first form: 2^65 canonical, past its integers", reasons_of( + seeded("36893488147419103000", "first")) == []) + check("first form: past its integers, not canonical", reasons_of( + seeded("36893488147419103001", "first")) == ["inexact_number"]) + check("receipt_form reads numbers as canonical", reasons_of( + _seal(seed, req.replace('"shots":10', '"shots":10,"seed":9007199254740993'), res, "first") + .replace(b'"alg":"ed25519"', b'"alg":"ed448"')) == ["inexact_number", "receipt_form"]) + check("inexact_number not in the receipt", reasons_of( + _seal(seed, req, res, receipt_extra={"x": 0}).replace(b'"x":0', b'"x":9007199254740993')) == []) + check("other method may not be simulated", reasons_of(_seal(seed, req, res.replace( + '"exact"', '"simulated"'))) == ["provider_class"]) + check("unknown class is not a reason", reasons_of(_seal(seed, req, res.replace('"exact"', '"novel"'))) == []) + check("no_receipt alone", reasons_of(b'{"request":{},"result":{"method":"provider"}}') == ["no_receipt"]) + check("not_a_job", reasons_of(b'{"request":[],"result":{}}') == ["not_a_job"]) + # The identity key with R = [S]B holds for every digest unless the key rules refuse it. + anything = _seal(seed, req, res) + forged = json.loads(anything.decode()) + forged["result"]["receipt"]["key"] = _SMALL_ORDER[0] + forged["result"]["receipt"]["signature"] = (_encode_point(_mul(5, _B)) + (5).to_bytes(32, "little")).hex() + forged = json.dumps(forged).encode() + check("identity key: bad_key", reasons_of(forged) == ["bad_key"]) + + # §8.2: R of small order, in a signature whose equation holds, so that only the + # rules on R can refuse it: R the identity, k = SHA-512(R ‖ A ‖ digest) mod l, + # S = k·a mod l, a the clamped scalar of the demo seed. + demo = bytes([42] * 32) + demo_scalar = (int.from_bytes(hashlib.sha512(demo).digest()[:32], "little") & ((1 << 254) - 8)) | (1 << 254) + demo_key = _encode_point(_mul(demo_scalar, _B)) + demo_digest = seal_digest(2, text_of(read_json(req.encode()), True).encode(), + text_of(read_json(res.encode()), True).encode()) + + def small_r_body(r_bytes): + k = int.from_bytes(hashlib.sha512(r_bytes + demo_key + demo_digest).digest(), "little") % _L + s_value = k * demo_scalar % _L + holds = _same_point(_mul(s_value, _B), _add(_IDENTITY, _mul(k, _decode_point(demo_key)))) + signature = r_bytes + s_value.to_bytes(32, "little") + return holds, _seal(demo, req, res, receipt_extra={"signature": signature.hex()}) + + identity_holds, identity_r_body = small_r_body(bytes.fromhex(_SMALL_ORDER[0])) + alias_r = (_P + 1).to_bytes(32, "little") # the identity again, spelt y = p + 1 + alias_holds, alias_r_body = small_r_body(alias_r) + check("identity R: the equation holds", identity_holds) + check("identity R: bad_signature", reasons_of(identity_r_body) == ["bad_signature"]) + check("identity R spelt y = p + 1: refused by its decoding", _decode_point(alias_r) is None) + check("identity R spelt y = p + 1: the equation holds", alias_holds) + check("identity R spelt y = p + 1: bad_signature", reasons_of(alias_r_body) == ["bad_signature"]) + + # A provider's stated result (§8.1, §8.3, §8.4). + def provider_body(request=_PROVIDER_REQUEST, **changes): + return _seal(seed, request, _provider_result(**changes)) + + names = {"0": "Q0"} + nobody = {"stated_by": "nobody", "mitigation": "unknown", "twirling": "unknown", "decoupling": "unknown", + "remapping": "unknown", "optimisation": "unknown", "verbatim": None} + two_to_53, two_to_54 = 2 ** 53, 2 ** 54 + prx_row = ROWS["prx-circuits"][0][2] + processor_row, simulator_row = ROWS["cloud-tasks"][0][2], ROWS["cloud-tasks"][1][2] + noise_row = dict(ROWS["native-turns"][1][2], sent={ + "fields": ROWS["native-turns"][1][2]["sent"]["fields"], + "settings": {"/input/gateset": "native", "/settings/error_mitigation/debiasing": False, + "/noise/model": "a device's model"}}) + + cases = ( + ("stated provider result", provider_body(), []), + ("class measured on a simulator", provider_body(accuracy__class="measured"), ["provider_class"]), + ("approximate false", provider_body(accuracy__approximate=False), ["provider_class"]), + ("approximate true", provider_body(accuracy__approximate=True), []), + ("silicon is measured", provider_body(provider__provenance="silicon", accuracy__class="measured"), []), + ("request names another", provider_body(provider__name="other"), ["request_provider"]), + ("request shots", provider_body(request=_PROVIDER_REQUEST.replace('"shots":4', '"shots":5')), ["request_shots"]), + ("default 1024 shots", provider_body(request=_PROVIDER_REQUEST.replace(',"shots":4', '')), ["request_shots"]), + ("counts sum", provider_body(counts={"1": 3}), ["counts"]), + ("zero count", provider_body(counts={"1": 4, "0": 0}, distinct_outcomes=2), ["counts"]), + ("describe missing", provider_body(provider__describe=_DELETE), ["provider_members"]), + ("estimate null", provider_body(cost__estimate=None), ["provider_members"]), + ("counts_source of another row", provider_body(provider__kind="prx-circuits"), ["counts_source", "execution"]), + ("http: client-stated", provider_body(provider__kind="http", execution=dict( + prx_row)), ["execution"]), + ("verbatim with remapping on", provider_body(execution__remapping="on"), ["execution"]), + ("execution extra member", provider_body(execution__extra="x"), ["execution"]), + ("provider may say unknown", provider_body(execution__twirling="unknown"), []), + ("provider may not say default", provider_body(execution__twirling="default"), ["execution"]), + ("nobody", provider_body(execution=nobody), []), + ("nobody on a kind with rows", provider_body(provider__kind="cloud-tasks", execution=nobody), []), + ("nobody on sampler-v2", provider_body(provider__kind="sampler-v2", execution=nobody), []), + ("nobody on a kind with rows, its source still the rows'", provider_body( + provider__kind="prx-circuits", execution=nobody), ["counts_source"]), + ("provider on a kind with rows", provider_body(provider__kind="cloud-tasks"), ["execution"]), + ("cloud-tasks processor row on silicon", provider_body( + provider__kind="cloud-tasks", provider__provenance="silicon", accuracy__class="measured", + compiled__qubit_names=names, execution=dict(processor_row, note="sent verbatim")), []), + ("cloud-tasks processor row on a simulator", provider_body( + provider__kind="cloud-tasks", compiled__qubit_names=names, execution=dict(processor_row)), ["execution"]), + ("cloud-tasks simulator row on an emulator", provider_body( + provider__kind="cloud-tasks", provider__provenance="emulator", compiled__qubit_names=names, + execution=dict(simulator_row)), []), + ("cloud-tasks simulator row on silicon", provider_body( + provider__kind="cloud-tasks", provider__provenance="silicon", accuracy__class="measured", + compiled__qubit_names=names, execution=dict(simulator_row)), ["execution"]), + ("row with its fields reordered", provider_body( + provider__kind="cloud-tasks", compiled__qubit_names=names, execution=dict( + simulator_row, sent={"fields": list(reversed(_CLOUD_TASKS_FIELDS)), "settings": {}})), ["execution"]), + # §8.4 compiled.qubit_names + ("qubit_names required: cloud-tasks by client", provider_body( + provider__kind="cloud-tasks", execution=dict(simulator_row)), ["provider_members"]), + ("qubit_names required: prx-circuits by client", provider_body( + provider__kind="prx-circuits", counts_source="vendor_counts", execution=dict(prx_row)), ["provider_members"]), + ("qubit_names given: prx-circuits by client", provider_body( + provider__kind="prx-circuits", counts_source="vendor_counts", compiled__qubit_names=names, + execution=dict(prx_row)), []), + ("qubit_names optional elsewhere, and held when given", provider_body(compiled__qubit_names=names), []), + ("qubit_names null is given", provider_body(compiled__qubit_names=None), ["provider_members"]), + ("qubit_names an array", provider_body(compiled__qubit_names=["Q0"]), ["provider_members"]), + ("qubit_names leading zero", provider_body(compiled__qubit_names={"0": "Q0", "01": "Q1"}), ["provider_members"]), + ("qubit_names key not an index", provider_body(compiled__qubit_names={"0": "Q0", "x": "Q1"}), ["provider_members"]), + ("qubit_names empty name", provider_body(compiled__qubit_names={"0": ""}), ["provider_members"]), + ("qubit_names name not a string", provider_body(compiled__qubit_names={"0": 7}), ["provider_members"]), + ("qubit_names without a layout entry", provider_body(compiled__qubit_names={"1": "Q1"}), ["provider_members"]), + ("qubit_names without a final_layout entry", provider_body( + compiled__final_layout=[1], compiled__qubit_names=names), ["provider_members"]), + ("qubit_names holds more than the layouts", provider_body(compiled__qubit_names={"0": "Q0", "5": "Q5"}), []), + ("qubit_names: an entry however written", provider_body( + compiled__layout=[1.0], compiled__final_layout=[1e0], compiled__qubit_names={"0": "Q0", "1": "Q1"}), []), + ("qubit_names: an entry in full", provider_body( + compiled__layout=[1e21], compiled__final_layout=[1e21], + compiled__qubit_names={"0": "Q0", "1000000000000000000000": "Q"}), []), + ("qubit_names: not as a number's text", provider_body( + compiled__layout=[1e21], compiled__final_layout=[1e21], + compiled__qubit_names={"0": "Q0", "1e+21": "Q"}), ["provider_members"]), + ("qubit_names not read in the earlier form", provider_body( + counts_source=_DELETE, execution=_DELETE, accuracy__class="measured", + compiled__qubit_names=None), []), + # §8.4: a whole number however large; only a count's value has a ceiling; a sum exact. + ("a whole number however large", provider_body(job__polls=1e300, compiled__swaps=two_to_53), []), + ("a count past its ceiling", provider_body( + request=_PROVIDER_REQUEST.replace('"shots":4', '"shots":%d' % two_to_53), + counts={"1": two_to_53}, shots=two_to_53), ["counts"]), + ("shots past a count's ceiling", provider_body( + request=_PROVIDER_REQUEST.replace('"shots":4', '"shots":%d' % two_to_54), + counts={"00": two_to_53 - 1, "01": two_to_53 - 1, "10": 2}, distinct_outcomes=3, shots=two_to_54), []), + ("a sum exact, not a double's", provider_body( + request=_PROVIDER_REQUEST.replace('"shots":4', '"shots":%d' % two_to_54), + counts={"00": two_to_53 - 1, "01": two_to_53 - 1, "10": 3}, distinct_outcomes=3, shots=two_to_54), ["counts"]), + # §8.4: "the request's" is by value. + ("the request's shots by value", provider_body( + request=_PROVIDER_REQUEST.replace('"shots":4', '"shots":4.0')), []), + ("two equal strings are the same", provider_body( + request=_PROVIDER_REQUEST.replace('"shots":4', '"shots":"4"'), shots="4"), ["counts"]), + ("a string is not a number", provider_body( + request=_PROVIDER_REQUEST.replace('"shots":4', '"shots":"4"')), ["request_shots"]), + ("native-turns with noise", provider_body(provider__kind="native-turns", counts_source="probabilities", + execution=noise_row), []), + ("native-turns with noise on silicon", provider_body( + provider__kind="native-turns", counts_source="probabilities", provider__provenance="silicon", + accuracy__class="measured", execution=noise_row), ["execution"]), + ("native-turns without noise on silicon", provider_body( + provider__kind="native-turns", counts_source="probabilities", provider__provenance="silicon", + accuracy__class="measured", execution=dict(ROWS["native-turns"][0][2])), []), + ("a later kind's settings names", provider_body(provider__kind="later", execution=dict( + prx_row, twirling="off", sent={"fields": ["a", "b~c"], "settings": { + "/b~0c/x": 1, "any_name": True}})), []), + ("a settings key ''", provider_body(provider__kind="later", execution=dict( + prx_row, twirling="off", sent={"fields": ["a"], "settings": {"": 1}})), ["execution"]), + ("a pointer outside the fields", provider_body(provider__kind="later", execution=dict( + prx_row, twirling="off", sent={"fields": ["a"], "settings": {"/z": 1}})), ["execution"]), + ("a pointer's rest not read", provider_body(provider__kind="later", execution=dict( + prx_row, twirling="off", sent={"fields": ["a"], "settings": {"/a/~2": 1}})), []), + ("negative omitted_shots", provider_body(omitted_shots=-1, counts={"1": 5}), ["counts"]), + ("negative swaps", provider_body(compiled__swaps=-1), ["provider_members"]), + ("compiled program hash", provider_body(compiled__program="00" * 32), ["compiled_program"]), + ("upper-case program hash", provider_body(compiled__program=blake3( + _PROVIDER_QASM3.encode()).hex().upper()), ["provider_members"]), + ("confirmed on a free run", provider_body(cost__confirmed=True), ["cost_confirmation"]), + ("earlier form must be measured", provider_body(counts_source=_DELETE, execution=_DELETE), ["provider_class"]), + ("earlier form, measured", provider_body(counts_source=_DELETE, execution=_DELETE, + accuracy__class="measured", provider__describe=_DELETE), []), + ) + for name, raw_case, want in cases: + check(name, reasons_of(raw_case) == want) + + # §8.4: qubit_names holds every index qasm3 acts on. + def with_qasm3(text, names_given): + return provider_body(compiled__qasm3=text, compiled__program=blake3(text.encode()).hex(), + compiled__qubit_names=names_given) + + names_short = with_qasm3("x $0;\ncz $0, $1;\n", {"0": "Q0"}) + for name, raw_case, want in ( + ("qasm3 acts on an index not named", names_short, ["provider_members"]), + ("qasm3's indices named", with_qasm3("x $0;\ncz $0, $1;\n", {"0": "Q0", "1": "Q1"}), []), + ("qasm3 $01 names 1", with_qasm3("x $0;\nx $01;\n", {"0": "Q0", "1": "Q1"}), []), + ("qasm3 $12 names 12", with_qasm3("x $0;\nx $12;\n", {"0": "Q0", "1": "Q1"}), ["provider_members"]), + ("qasm3: a $ with no digit names none", with_qasm3("x $0;\n// $ and $x\n", {"0": "Q0"}), []), + ("qasm3 with names absent", with_qasm3("x $0;\ncz $0, $1;\n", _DELETE), []), + ("qasm3: a long run of zeros", with_qasm3("x $" + "0" * 5000 + ";\n", {"0": "Q0"}), [])): + check(name, reasons_of(raw_case) == want) + + # §8.6: a set's receipt. + program_a = "OPENQASM 3.0;\nqubit[1] q;\nbit[1] c;\nx q[0];\nc = measure q;\n" + program_b = "OPENQASM 3.0;\nqubit[1] q;\nbit[1] c;\nc = measure q;\n" + programs_request = {"programs": [program_a, {"qasm": program_b, "shots": 5}], "shots": 10, "dry_run": False} + programs_items = [{"shots": 10, "qasm": program_a}, {"shots": 5, "qasm": program_b}] + + def programs_of(n): + return ({"programs": [program_a] * n, "shots": 10}, [{"shots": 10, "qasm": program_a}] * n) + + sweep_request = {"qasm": "OPENQASM 3.0;\ninput angle t;\nqubit[1] q;\nrx(t) q[0];\n", "shots": 10, + "sweep": {"t": [0.5, 1], "u": [1, 2, 3]}} + sweep_items = [{"shots": 10, "qasm": "OPENQASM 3.0;\nqubit[1] q;\nrx(%d) q[0];\n" % i} for i in range(6)] + + def set_of(n, **kw): + asked, items = programs_of(n) + return _set_body(seed, asked, items, **kw) + + def drop(member): + return lambda head, items: head.pop(member) + + def wrong_root(head, items): + head["root"] = "ab" * 32 + + def tamper_count(items): + items[0]["result"]["counts"] = {"1": 11} + + def tamper_after(head, items): + items[0]["result"]["counts"] = {"1": 11} + + def swap_path(head, items): + items[0]["path"][0] = "cd" * 32 + + def longer_path(head, items): + items[0]["path"].append(head["root"]) + + def index_off(head, items): + items[1]["index"] = 7 + + def both(head, items): + items[0]["error"] = "and an error" + + def upper_path(head, items): + items[0]["path"][0] = items[0]["path"][0].upper() + + def unsealed_members(head, items): + for i, item in enumerate(items): + item.update(status="done", method="statevector", why="a fixture", point={"t": i}) + + def drop_key(head, items): + head["_drop_key"] = True # _seal_set removes it, and the receipt's key + + def upper_key(head, items): + head["_upper_key"] = True + + def head_provider(head, items): + head.update({"method": "provider", "accuracy": {"class": "measured"}}) + + set_cases = ( + ("set of programs", _set_body(seed, programs_request, programs_items), []), + ("set of one", set_of(1), []), + ("set of three, one an error", _set_body(seed, programs_of(3)[0], programs_of(3)[1], errors=(1,)), []), + ("set of five", set_of(5), []), + ("set of a sweep", _set_body(seed, sweep_request, sweep_items), []), + ("set: sweep null is none", _set_body(seed, dict(programs_request, sweep=None), programs_items), []), + ("set: count wrong", set_of(2, head_extra={"count": 3}), ["set_items"]), + ("set: count missing", set_of(2, after_root=drop("count")), ["set_items"]), + ("set: count 2.0 is 2", set_of(2, head_extra={"count": 2.0}), []), + ("set: items emptied", set_of(2, after_root=lambda head, items: items.clear()), ["set_items"]), + ("set: an index off stops the next two", set_of(2, after_root=lambda h, it: (index_off(h, it), tamper_after(h, it))), + ["set_items"]), + ("set: result and error both", set_of(2, after_root=both), ["set_items"]), + ("set: a path digit upper-case", set_of(2, after_root=upper_path), ["set_items"]), + ("set: an item not the program's", _set_body( + seed, programs_request, [programs_items[0], dict(programs_items[1], shots=6)]), ["set_items"]), + ("set: an item more than the programs", _set_body(seed, programs_request, programs_items + programs_items[:1]), + ["set_items"]), + ("set: programs and sweep both", _set_body(seed, dict(programs_request, sweep={"t": [1]}), programs_items), + ["set_items"]), + ("set: neither, and no items", _set_body(seed, {"shots": 10}, []), ["set_items"]), + ("set: a sweep value empty", _set_body(seed, dict(sweep_request, sweep={"t": [], "u": [1]}), sweep_items[:0]), + ["set_items"]), + ("set: a sweep item's request otherwise", _set_body( + seed, sweep_request, sweep_items[:5] + [dict(sweep_items[5], shots=11)]), ["set_items"]), + ("set: a sweep of the wrong count", _set_body(seed, sweep_request, sweep_items[:5]), ["set_items"]), + ("set: an item tampered before its leaf", set_of(2, before_root=tamper_count), ["set_item"]), + ("set: an item tampered after the root", set_of(2, after_root=tamper_after), ["set_item", "set_root"]), + ("set: the root another", set_of(2, after_root=wrong_root), ["set_root"]), + ("set: a path's sibling another", set_of(3, after_root=swap_path), ["set_root"]), + ("set: a path too long", set_of(2, after_root=longer_path), ["set_root"]), + ("set: an item inexact", set_of(1, item_result=_ITEM_RESULT.replace('"gates":1', '"gates":9007199254740993')), + ["set_item"]), + ("set: its request inexact", _set_body( + seed, dict(programs_of(1)[0], dry_run=Num("9007199254740993", 2.0 ** 53)), programs_of(1)[1]), + ["inexact_number"]), + ("set: no provider rules on a head", set_of(1, after_root=head_provider), []), + ("set: an error item writes 2^53 + 1", _set_body( + seed, dict(programs_of(1)[0], seed=2 ** 53), + [dict(programs_of(1)[1][0], seed=Num("9007199254740993", 2.0 ** 53))], errors=(0,)), ["set_item"]), + ("set: an error item writes 2^60 canonically", _set_body( + seed, dict(programs_of(1)[0], seed=2 ** 60), + [dict(programs_of(1)[1][0], seed=Num("1152921504606847000", 2.0 ** 60))], errors=(0,)), []), + ("set: first form, an error item's u64 read as canonical", _set_body( + seed, dict(programs_of(1)[0], seed=2 ** 53), + [dict(programs_of(1)[1][0], seed=Num("9007199254740993", 2.0 ** 53))], errors=(0,), form="first"), + ["set_item"]), + ("set: first form, an error item's exact u64", _set_body( + seed, dict(programs_of(1)[0], seed=2 ** 53), + [dict(programs_of(1)[1][0], seed=2 ** 53)], errors=(0,), form="first"), []), + ("set: first form, an error item past u64", _set_body( + seed, dict(programs_of(1)[0], seed=2 ** 64), + [dict(programs_of(1)[1][0], seed=Num("18446744073709551617", 2.0 ** 64))], errors=(0,), form="first"), + ["set_item"]), + ("set: first form sealed", set_of(2, form="first"), []), + ("set: an item signed by another key", set_of(2, item_seed=bytes([7] * 32)), ["set_item"]), + ("set: an item's result a set's", set_of(1, item_result=_ITEM_RESULT.replace( + '"method"', '"format":"wai.quantum.set/1","method"')), ["set_item"]), + ("set: an item's unsealed members", set_of(2, after_root=unsealed_members), []), + ("set: an item's unsealed members changed", set_of(2, after_root=lambda h, it: ( + unsealed_members(h, it), it[0].update(status="failed", method=7, why=None, point=[]))), []), + ("set: an item receipt's own extra member", set_of(2, after_root=lambda h, it: ( + it[0]["result"]["receipt"].update(note="ignored", x=[1, 2]))), []), + ("set: its receipt with no key", set_of(1, after_root=drop_key), ["receipt_form", "set_item"]), + ("set: its key in upper case", set_of(1, after_root=upper_key), ["receipt_form", "set_item"]), + ) + for name, raw_case, want in set_cases: + check(name, reasons_of(raw_case) == want) + sealed_set = set_of(2) + check("set: the head tampered", reasons_of(sealed_set.replace(b'"count": 2', b'"count": 3')) + == ["digest_mismatch", "set_items"]) + unlisted = json.loads(sealed_set.decode()) + del unlisted["result"]["items"] + check("set: items missing", reasons_of(json.dumps(unlisted).encode()) == ["set_items"]) + check("set: no receipt, alone", reasons_of(json.dumps( + {"request": {}, "result": {"format": SET_FORMAT, "items": 5}}).encode()) == ["no_receipt"]) + + # The scan of sent.settings against sent.fields is linear. Every key names the last + # field, so a scan of the list walks all of it each time (tens of seconds). + many_fields = ["f%06d" % i for i in range(50000)] + many_settings = dict(("/%s/k%d" % (many_fields[-1], i), i) for i in range(50000)) + many = dict(prx_row, twirling="off", sent={"fields": many_fields, "settings": many_settings}) + started = time.perf_counter() + many_hold = execution_rules_hold(many) + check("50,000 fields and 50,000 settings, within 3 s", + many_hold and time.perf_counter() - started < 3.0) + + # Each plant changes a verdict a differential would compare. + def planted(plant, raw): + PLANTS.add(plant) + try: + return judge(raw)["reasons"] + finally: + PLANTS.discard(plant) + + simulator_measured = provider_body(accuracy__class="measured") + duplicated = body.replace(b'{"request"', b'{"why":"a","why":"b","request"') + plant_cases = ( + ("lenient-key", forged, ["bad_key"], []), + ("no-alg", body.replace(b'"alg":"ed25519"', b'"alg":"ed448"'), ["receipt_form"], []), + ("last-duplicate", duplicated, ["unreadable"], []), + ("no-inexact", big, ["inexact_number"], []), + ("measured-simulator", simulator_measured, ["provider_class"], []), + ("whole-cap", provider_body(job__polls=two_to_53), [], ["provider_members"]), + ("lax-small-order-r", identity_r_body, ["bad_signature"], []), + ("no-canonical-spelling", seeded("1152921504606847000"), [], ["inexact_number"]), + ("set-seal-only", set_of(2, head_extra={"count": 3}), ["set_items"], []), + ("names-layout-only", names_short, ["provider_members"], []), + ) + for plant, raw, strict, lenient in plant_cases: + check("plant %s: strict verdict" % plant, reasons_of(raw) == strict) + check("plant %s: planted verdict" % plant, planted(plant, raw) == lenient) + check("every plant is tested", set(c[0] for c in plant_cases) == set(PLANTS_KNOWN)) + # Its decoding still refuses the alias, with the small-order rule off. + check("plant lax-small-order-r: y = p + 1 still refused", + planted("lax-small-order-r", alias_r_body) == ["bad_signature"]) + + failed = [name for name, ok in results if not ok] + for name in failed: + sys.stdout.write("FAIL %s\n" % name) + sys.stdout.write("self-test: %d checks, %d failed\n" % (len(results), len(failed))) + return 1 if failed else 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/wai/quantum-studio/locales/en/hardware.json b/wai/quantum-studio/locales/en/hardware.json index 56193603a..96b580b0c 100644 --- a/wai/quantum-studio/locales/en/hardware.json +++ b/wai/quantum-studio/locales/en/hardware.json @@ -7,6 +7,7 @@ "hardware.note": "Runs on your own hardware go through the desktop app or the command line (wai-quantum run --provider NAME): a browser tab must not hold the credentials a provider needs.", "hardware.cost_unknown": "cost unknown: {why}", "hardware.free": "free", + "hardware.at_least": "at least {amount}", "hardware.billed_by": " (billed by the {unit})", "hardware.about_seconds": ", about {seconds} s", "hardware.confirm_run": "Run on {name} ({device}, {provenance})?", diff --git a/wai/quantum-studio/locales/zh-CN/hardware.json b/wai/quantum-studio/locales/zh-CN/hardware.json index 4de91d3d3..d0082c863 100644 --- a/wai/quantum-studio/locales/zh-CN/hardware.json +++ b/wai/quantum-studio/locales/zh-CN/hardware.json @@ -7,6 +7,7 @@ "hardware.note": "在你自己的硬件上运行,须经由桌面应用或命令行(wai-quantum run --provider NAME):浏览器标签页不得持有提供方所需的凭据。", "hardware.cost_unknown": "费用未知:{why}", "hardware.free": "免费", + "hardware.at_least": "至少 {amount}", "hardware.billed_by": "(按 {unit} 计费)", "hardware.about_seconds": ",约 {seconds} 秒", "hardware.confirm_run": "在 {name}({device},{provenance})上运行?", diff --git a/wai/quantum-studio/src/engine.js b/wai/quantum-studio/src/engine.js index a8f9052f3..5927b7a46 100644 --- a/wai/quantum-studio/src/engine.js +++ b/wai/quantum-studio/src/engine.js @@ -1,3 +1,4 @@ +import { jobText } from './job-text.js'; // Where a job runs. Three engines, one interface, one engine underneath: // // tab the job service's engine compiled for the browser, in a worker @@ -240,7 +241,7 @@ export class DesktopEngine { async lint(qasm) { return JSON.parse(await this.invoke('lint', { qasm })); } draw(qasm, width) { return this.call('draw', { qasm, width }); } layout(qasm) { return this.call('layout', { qasm }); } - verify(job) { return this.call('verify', { job: JSON.stringify(job) }); } + verify(job) { return this.call('verify', { job: jobText(job) }); } // 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 }); } diff --git a/wai/quantum-studio/src/hardware.js b/wai/quantum-studio/src/hardware.js index eb25ff2e7..ef00c5d02 100644 --- a/wai/quantum-studio/src/hardware.js +++ b/wai/quantum-studio/src/hardware.js @@ -25,7 +25,8 @@ export function costText(plan) { if (e.free === true) return tr('hardware.free'); const unit = e.unit ? tr('hardware.billed_by', { unit: e.unit }) : ''; const secs = e.seconds !== undefined ? tr('hardware.about_seconds', { seconds: e.seconds }) : ''; - return `${e.amount} ${e.currency}${unit}${secs}`; + const amount = `${e.amount} ${e.currency}`; + return `${e.at_least ? tr('hardware.at_least', { amount }) : amount}${unit}${secs}`; } // What the dialog says before a run that is not free. diff --git a/wai/quantum-studio/src/job-text.js b/wai/quantum-studio/src/job-text.js new file mode 100644 index 000000000..a6ca6d0c2 --- /dev/null +++ b/wai/quantum-studio/src/job-text.js @@ -0,0 +1,6 @@ +// A job to verify, as the engine is given it: its text as it was given, when +// there is one, since a receipt is checked on its text; else the object +// written out, which is all there is. JavaScript writes a member named by +// digits ("11") ahead of the others and a large whole number by its shortest +// digits, so an object written out again can differ from what was sealed. +export const jobText = (job) => (typeof job === 'string' ? job : JSON.stringify(job)); diff --git a/wai/quantum-studio/src/qec-view.js b/wai/quantum-studio/src/qec-view.js index 4e4936869..e8e4e3135 100644 --- a/wai/quantum-studio/src/qec-view.js +++ b/wai/quantum-studio/src/qec-view.js @@ -176,12 +176,16 @@ export function qecPane({ engine }) { summary.textContent = `Reading ${code.value} and certifying its distance…`; try { const body = await engine.qec('code', { code: code.value }); + // The seal is checked before anything of the code is shown, so the pane + // changes in one step: its summary, decoders, certificate and lattice are + // never one code's beside another's, and a click meanwhile flips a qubit + // of the code still drawn. + const ok = await verified(body); const c = body.result.code, k = body.result.certificate; current = { code: c, certificate: k }; for (const s of Object.values(state)) s.clear(); decoder.replaceChildren(...c.decoders.map((d) => el('option', { value: d, text: DECODER_NAMES[d] ?? d }))); summary.textContent = `${c.name}: [[${c.n}, ${c.k}, ${k?.distance ?? '?'}]], ${c.checks.length} Z checks (shown) and ${c.stabs.length} X stabilizers.`; - const ok = await verified(body); cert.textContent = k ? `${k.reading} ${k.x.nodes + k.z.nodes} search nodes; ${ok ? 'the certificate is sealed and its seal verifies here' : 'its seal DOES NOT VERIFY here'}.` : 'Not certified.'; result.textContent = 'Click qubits to flip them, then Decode.'; draw(); @@ -215,10 +219,11 @@ export function qecPane({ engine }) { try { const body = await engine.qec('threshold', { family: family.value, distances: circuit ? [3, 5] : [3, 5, 7], p: circuit ? [0.003, 0.006, 0.009] : [0.02, 0.05, 0.08], shots: n }); const r = body.result; + // The seal is checked first, so the plot and its reading change together. + const ok = await verified(body); plot.replaceChildren(thresholdSvg(r.points)); const below = r.below_threshold.filter((b) => b.distance_helps).map((b) => b.p); const fit = r.fit ? ` The code model fitted to these points: a = ${r.fit.prefactor.toFixed(4)}, p* = ${r.fit.threshold.toFixed(5)}.` : ''; - const ok = await verified(body); reading.textContent = `${r.noise}; decoder ${r.decoder}. ${below.length ? `At p = ${below.join(', ')} every larger distance fails less: below this decoder’s threshold.` : 'At no rate here does every larger distance fail less.'}${fit} Each point has its standard error in its title; few shots make a crossing a guess. ${ok ? 'Sealed; the seal verifies here.' : 'The seal DOES NOT VERIFY here.'}`; } catch (e) { reading.textContent = e.message; } finally { busy = false; measure.disabled = false; } diff --git a/wai/quantum-studio/src/worker.js b/wai/quantum-studio/src/worker.js index fba6efab8..91a614d43 100644 --- a/wai/quantum-studio/src/worker.js +++ b/wai/quantum-studio/src/worker.js @@ -1,3 +1,4 @@ +import { jobText } from './job-text.js'; // The job service's engine, compiled for the browser, off the page's thread. // The page posts {id, op, pkg, seed, memoryMb, ...}; the first message loads // the engine package from `pkg`. Answers are {id, ok, value} or {id, ok: false, @@ -36,7 +37,7 @@ self.onmessage = async (ev) => { case 'lint': text = mod.engine_lint(ev.data.qasm); break; case 'draw': text = mod.engine_draw(ev.data.qasm, ev.data.width ?? 100); break; case 'layout': text = mod.engine_layout(ev.data.qasm); break; - case 'verify': text = mod.engine_verify(JSON.stringify(ev.data.job)); break; + case 'verify': text = mod.engine_verify(jobText(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; diff --git a/wai/quantum-studio/test/hardware.test.mjs b/wai/quantum-studio/test/hardware.test.mjs index 0e0e7eeba..67201025d 100644 --- a/wai/quantum-studio/test/hardware.test.mjs +++ b/wai/quantum-studio/test/hardware.test.mjs @@ -17,6 +17,7 @@ const priced = plan(false, { free: false, amount: '0.3', currency: 'USD', unit: check(needsConfirmation(priced) && !needsConfirmation(plan(true, { free: true })), 'a run not stated free needs a confirmation, a free one does not'); check(needsConfirmation(plan(undefined, { unknown: 'no estimate' })), 'an unknown cost needs a confirmation'); check(costText(priced) === '0.3 USD (billed by the shot), about 4 s', `the cost in words (${costText(priced)})`); +check(costText(plan(false, { free: false, amount: '0.003750', currency: 'USD', unit: 'minute', at_least: true })) === 'at least 0.003750 USD (billed by the minute)', 'a minimum says it is one'); check(costText(plan(false, { unknown: 'the provider gives no estimate' })) === 'cost unknown: the provider gives no estimate', 'an unknown cost says so'); const words = confirmText(priced).join(' '); check(/Run on lab \(reference:line-5/.test(words) && /not free: 0\.3 USD/.test(words) && /12 native gates in rzsxxcz/.test(words) && /spends your account/.test(words), `the dialog's words (${words})`); diff --git a/wai/quantum-studio/test/verify-text.test.mjs b/wai/quantum-studio/test/verify-text.test.mjs new file mode 100644 index 000000000..35db357aa --- /dev/null +++ b/wai/quantum-studio/test/verify-text.test.mjs @@ -0,0 +1,22 @@ +// A receipt is checked on its text: what the person or the engine gave, never a +// JavaScript object written out again, which moves a member named by digits +// ahead of the others and rewrites a large whole number. +// +// node test/verify-text.test.mjs +let fails = 0; +const check = (ok, what) => { console.log(`${ok ? 'PASS' : 'FAIL'} ${what}`); if (!ok) fails += 1; }; + +const calls = []; +globalThis.window = { __TAURI__: { core: { invoke: async (cmd, args) => { calls.push([cmd, args]); return JSON.stringify({ valid: true }); } } } }; +const { DesktopEngine } = await import('../src/engine.js'); +const desk = new DesktopEngine(); +const text = '{"request":{"x":1},"result":{"counts":{"00":1,"11":2},"receipt":{}}}'; +await desk.verify(text); +check(calls.at(-1)?.[1]?.job === text, 'the desktop app is given the receipt\'s text as it is'); +await desk.verify(JSON.parse(text)); +check(typeof calls.at(-1)?.[1]?.job === 'string', 'an object, all there is, is written out'); +const { jobText } = await import('../src/job-text.js'); +check(jobText(text) === text && jobText({ a: 1 }) === '{"a":1}', 'the worker sends a text as it is, an object written out'); + +if (fails) { console.log(`${fails} check(s) failed`); process.exit(1); } +console.log('verify-text: every check held'); diff --git a/wai/tools/quantum-docs/fixtures/keys.txt b/wai/tools/quantum-docs/fixtures/keys.txt new file mode 100644 index 000000000..eff217a62 --- /dev/null +++ b/wai/tools/quantum-docs/fixtures/keys.txt @@ -0,0 +1 @@ +an-id-for-the-documentation-only:a-secret-for-the-documentation-only diff --git a/wai/wai-quantum-cli/Cargo.lock b/wai/wai-quantum-cli/Cargo.lock index dddf3bf30..20534ba61 100644 --- a/wai/wai-quantum-cli/Cargo.lock +++ b/wai/wai-quantum-cli/Cargo.lock @@ -1241,10 +1241,11 @@ dependencies = [ [[package]] name = "wai-quantum-qpu" -version = "0.4.1" +version = "0.5.0" dependencies = [ "blake3", "serde_json", + "sha2", "wai-quantum", ] diff --git a/wai/wai-quantum-cli/Cargo.toml b/wai/wai-quantum-cli/Cargo.toml index 3117b31c1..bd1d47622 100644 --- a/wai/wai-quantum-cli/Cargo.toml +++ b/wai/wai-quantum-cli/Cargo.toml @@ -42,4 +42,4 @@ getrandom = "0.4" serde_json = { version = "1", features = ["float_roundtrip"] } ureq = { version = "3", optional = true, features = ["json"] } wai-quantum = { version = "0.8.1", path = "../wai-quantum", default-features = false, features = ["full"] } -wai-quantum-qpu = { version = "0.4.1", path = "../wai-quantum-qpu", optional = true } +wai-quantum-qpu = { version = "0.5.0", path = "../wai-quantum-qpu", optional = true } diff --git a/wai/wai-quantum-cli/README.md b/wai/wai-quantum-cli/README.md index 25e471037..4f1fada1e 100644 --- a/wai/wai-quantum-cli/README.md +++ b/wai/wai-quantum-cli/README.md @@ -269,7 +269,7 @@ wai-quantum providers cancel pj-0123456789abcdef # cancels it; one that fini A request that did not complete may have reached the provider, so it is never sent again: that could be a second bill. The record says so, and you look in the provider's own list. A job collected later is read against the program that was sent, whatever the device's calibration has become since. A provider run as a local process has to keep its jobs itself, or they end with it. -A credential is read from standard input (`--credential-stdin`) or named by an environment variable (`--credential-env VAR`), never given on a command line. `WAI_QUANTUM_CREDENTIALS=file` keeps it in a file only you can read instead of the keychain. A provider run's result is labelled `measured`, sealed with your key with the provider, the calibration snapshot, the compiled program's hash and the counts set beside the circuit's exact distribution, and checked by `verify`. `wai-quantum providers reference` serves the reference provider, the template for your own. +A credential is read from standard input (`--credential-stdin`) or named by an environment variable (`--credential-env VAR`), never given on a command line. `WAI_QUANTUM_CREDENTIALS=file` keeps it in a file only you can read instead of the keychain. A provider run's result is labelled `measured` from a processor and `simulated` from a provider's simulator, and says where its counts come from (`counts_source`) and what was set off at the provider (`execution`). It is sealed with your key with the provider, the calibration snapshot, the compiled program's text and hash and the counts set beside the circuit's exact distribution, and checked by `verify`, which names every rule a receipt breaks (`verify --json` gives the reasons as data; `verify --lines` judges one job a line). `wai-quantum providers reference` serves the reference provider, the template for your own. ## Error correction diff --git a/wai/wai-quantum-cli/README.zh-CN.md b/wai/wai-quantum-cli/README.zh-CN.md index 0212f4668..761d8811c 100644 --- a/wai/wai-quantum-cli/README.zh-CN.md +++ b/wai/wai-quantum-cli/README.zh-CN.md @@ -267,7 +267,7 @@ wai-quantum providers cancel pj-0123456789abcdef # 取消它;已先完成 未完成的请求可能已经到达提供方,所以绝不会再次发送:那可能造成第二笔费用。记录会说明这一点,由你去查看提供方自己的列表。稍后取回的作业,是对照当初发送的程序读取的,无论设备的校准此后变成了什么样。以本地进程运行的提供方必须自己保存作业,否则作业会随进程一起结束。 -凭据从标准输入读取(`--credential-stdin`),或由环境变量指定(`--credential-env VAR`),绝不在命令行上给出。`WAI_QUANTUM_CREDENTIALS=file` 把它保存在只有你能读取的文件中,而不是钥匙串里。在提供方上运行的结果标为 `measured`,用你的密钥封存,并附有提供方、校准快照、编译后程序的哈希,以及与线路精确分布并列的计数,可由 `verify` 检查。`wai-quantum providers reference` 提供参考提供方,作为你自己的提供方的模板。 +凭据从标准输入读取(`--credential-stdin`),或由环境变量指定(`--credential-env VAR`),绝不在命令行上给出。`WAI_QUANTUM_CREDENTIALS=file` 把它保存在只有你能读取的文件中,而不是钥匙串里。在提供方上运行的结果,来自处理器的标为 `measured`,来自提供方模拟器的标为 `simulated`,并写明计数的来源(`counts_source`)以及在提供方处关闭了什么(`execution`)。结果用你的密钥封存,并附有提供方、校准快照、编译后程序的文本及其哈希,以及与线路精确分布并列的计数,可由 `verify` 检查;它会指出回执违反的每一条规则(`verify --json` 以数据形式给出原因,`verify --lines` 每行判定一个作业)。`wai-quantum providers reference` 提供参考提供方,作为你自己的提供方的模板。 ## 纠错 diff --git a/wai/wai-quantum-cli/docs/llms-full.txt b/wai/wai-quantum-cli/docs/llms-full.txt index 4ab3f937f..b6a117fa0 100644 --- a/wai/wai-quantum-cli/docs/llms-full.txt +++ b/wai/wai-quantum-cli/docs/llms-full.txt @@ -224,10 +224,10 @@ Parameters: ### `verify_quantum_result`: Check a result's receipt -Whether a job's receipt seals its request and its result, as run_quantum_circuit or the job service returns them, and which key signed it. +Whether a job's receipt seals its request and its result, as run_quantum_circuit or the job service returns them, and every rule of its form holds (`reasons` names what does not), and which key signed it. Parameters: -- `job` (object, required): The job: {"request": ..., "result": ...}, the result carrying its receipt. +- `job` (any, required): The job's text as it was given (preferred: read as the protocol reads it, so a member named twice or a number past what a parsed job keeps is seen), or the job: {"request": ..., "result": ...}, the result carrying its receipt. ### `discover_evaluate`: Judge a generator against a workbench specification @@ -588,7 +588,7 @@ Parameters: ### `run_on_provider`: Run on a provider (can spend the person's money) -Runs a program on one of the person's own quantum providers: their device, their account or their simulator. THIS CAN SPEND THE PERSON'S MONEY OR QUOTA. A run that is not free, or whose cost is unknown, is refused unless confirm_cost is true; set it only after the person has seen the estimate (provider_estimate) and agreed to that run. The answer is a job in the service's form: counts keyed by the classical bits, measured on the device and not reproducible by recomputation, with the circuit's exact distribution beside them, sealed in a receipt with the compiled program's hash, the provider, its protocol and the device's calibration snapshot (verify_quantum_result checks it). A device's queue can be long: if the job has not finished within this call the answer has status running and an id, nothing is cancelled and nothing is held, and get_provider_job collects it later, from any session, for as long as the provider keeps it. Do not run it again to find out: that is a second job, and it can be a second bill. +Runs a program on one of the person's own quantum providers: their device, their account or their simulator. THIS CAN SPEND THE PERSON'S MONEY OR QUOTA. A run that is not free, or whose cost is unknown, is refused unless confirm_cost is true; set it only after the person has seen the estimate (provider_estimate) and agreed to that run. The answer is a job in the service's form: counts keyed by the classical bits, labelled measured from a processor and simulated from a simulator, not reproducible by recomputation, with where they come from (counts_source), what was set off at the provider (execution) and the circuit's exact distribution beside them, sealed in a receipt with the compiled program's text and hash, the provider, its protocol and the device's calibration snapshot (verify_quantum_result checks it). A device's queue can be long: if the job has not finished within this call the answer has status running and an id, nothing is cancelled and nothing is held, and get_provider_job collects it later, from any session, for as long as the provider keeps it. Do not run it again to find out: that is a second job, and it can be a second bill. Parameters: - `provider` (string, required): The provider's name in the person's registry (providers_list). @@ -703,11 +703,12 @@ Parameters: ### `verify_quantum_receipt`: Verify a result's receipt -Whether a result's receipt (an Ed25519 signature by the service) seals exactly that request and that result. Pass the job's own `request` (as run_quantum_circuit or get_quantum_job returns it: a run here asks for its verdict, so the request it seals says so) and its result. +Whether a result's receipt (an Ed25519 signature) seals exactly that request and that result, and every rule of its form holds (`reasons` names what does not). Pass the job's own `request` (as run_quantum_circuit or get_quantum_job returns it: a run here asks for its verdict, so the request it seals says so) and its result. Parameters: -- `request` (object, required) -- `result` (object, required) +- `job` (string): The job's text as it was given (preferred: read as the protocol reads it, so a member named twice or a number past what a parsed job keeps is seen) +- `request` (object) +- `result` (object) ### `critique_qasm`: Critique a quantum program @@ -1853,7 +1854,8 @@ Options: --provider Run on one of your own providers (`wai-quantum providers list`) instead of here: a device - answers counts, sealed as measured on it + answers counts, sealed as measured on it, or as simulated when the provider's device is a + simulator --dry-run With --provider: the plan (the exact compile to the device's gates and its check by @@ -3574,6 +3576,10 @@ Options: 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 + --json + A job's verdict as JSON: valid, the reasons (wai/extensions/quantum-providers.md section + 8.4), the key, the receipt's form, and what a provider's result says of itself + --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 @@ -3582,6 +3588,10 @@ Options: 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 + --lines + One job a line, each judged as a job's receipt, one JSON verdict a line; the status is 1 + when any is not valid + -h, --help Print help @@ -6968,11 +6978,11 @@ Options: read as its family's circuit at those values --kind - sampler-v2, prx-circuits or native-turns (the built-in protocols), http or process (the - open provider protocol) + sampler-v2, prx-circuits, native-turns or cloud-tasks (the built-in protocols), http or + process (the open provider protocol) --endpoint - http: the provider's URL; a built-in kind: another endpoint than its own + http: the provider's URL; a built-in kind but cloud-tasks: another endpoint than its own --lang The language of the words the command line writes itself, en or zh-CN (by default the one diff --git a/wai/wai-quantum-cli/docs/local-mcp.json b/wai/wai-quantum-cli/docs/local-mcp.json index c2fa94a3e..bf7618243 100644 --- a/wai/wai-quantum-cli/docs/local-mcp.json +++ b/wai/wai-quantum-cli/docs/local-mcp.json @@ -1459,9 +1459,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -1815,7 +1820,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -1830,6 +1835,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -3245,7 +3262,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -3266,24 +3283,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -3298,17 +3318,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -3318,6 +3341,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -4151,7 +4283,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -4172,19 +4304,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -5087,9 +5222,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -5443,7 +5583,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -5458,6 +5598,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -6873,7 +7025,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -6894,24 +7046,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -6926,17 +7081,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -6946,6 +7104,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -18053,7 +18320,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -18074,19 +18341,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -19507,7 +19777,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -19528,19 +19798,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -21092,7 +21365,7 @@ { "name": "verify_quantum_result", "title": "Check a result's receipt", - "description": "Whether a job's receipt seals its request and its result, as run_quantum_circuit or the job service returns them, and which key signed it.", + "description": "Whether a job's receipt seals its request and its result, as run_quantum_circuit or the job service returns them, and every rule of its form holds (`reasons` names what does not), and which key signed it.", "inputSchema": { "type": "object", "required": [ @@ -21100,8 +21373,11 @@ ], "properties": { "job": { - "type": "object", - "description": "The job: {\"request\": ..., \"result\": ...}, the result carrying its receipt." + "type": [ + "object", + "string" + ], + "description": "The job's text as it was given (preferred: read as the protocol reads it, so a member named twice or a number past what a parsed job keeps is seen), or the job: {\"request\": ..., \"result\": ...}, the result carrying its receipt." } } }, @@ -21115,7 +21391,7 @@ "anyOf": [ { "type": "object", - "description": "Whether a job's receipt seals its request and its result, and the key that signed it.", + "description": "Whether a job's receipt seals its request and its result and every rule of its form holds, and the key that signed it.", "required": [ "valid", "key" @@ -21124,12 +21400,96 @@ "valid": { "type": "boolean" }, + "reasons": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Each rule the receipt breaks (the provider protocol's section 8.4): none when it is valid." + }, + "form": { + "type": [ + "string", + "null" + ], + "description": "The receipt's form: rfc8785, or first." + }, + "provider": { + "type": "object", + "description": "For a provider's result, what it says of itself." + }, "key": { "type": [ "string", "null" ], "description": "The signing key, 32 bytes in hex." + }, + "signer": { + "type": [ + "string", + "null" + ], + "description": "That key as a did:key." + } + } + }, + { + "type": "object", + "description": "A set's check: the set's receipt over its request and head, the items the request gives at their indices, every item's own receipt, the root over the leaves and each item's path.", + "required": [ + "valid", + "signed", + "derived", + "items", + "items_sealed", + "root" + ], + "properties": { + "valid": { + "type": "boolean" + }, + "signed": { + "type": "boolean" + }, + "derived": { + "type": "boolean" + }, + "items": { + "type": "integer", + "minimum": 0 + }, + "items_sealed": { + "type": "boolean" + }, + "root": { + "type": "boolean" + }, + "key": { + "type": [ + "string", + "null" + ] + }, + "signer": { + "type": [ + "string", + "null" + ], + "description": "That key as a did:key, where a surface gives it." + }, + "reasons": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Each rule the set's receipt breaks (the provider protocol's sections 8.4 and 8.6): none when it is valid." + }, + "form": { + "type": [ + "string", + "null" + ] } } }, @@ -31263,9 +31623,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -31619,7 +31984,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -31634,6 +31999,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -33049,7 +33426,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -33070,24 +33447,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -33102,17 +33482,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -33122,6 +33505,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -33955,7 +34447,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -33976,19 +34468,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -34891,9 +35386,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -35247,7 +35747,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -35262,6 +35762,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -36677,7 +37189,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -36698,24 +37210,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -36730,17 +37245,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -36750,6 +37268,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -37815,7 +38442,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -37836,19 +38463,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -37970,7 +38600,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -37991,19 +38621,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -38126,7 +38759,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -38147,19 +38780,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -38568,7 +39204,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -38589,19 +39225,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -38723,7 +39362,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -38744,19 +39383,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -38879,7 +39521,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -38900,19 +39542,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -39275,7 +39920,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -39296,19 +39941,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -39430,7 +40078,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -39451,19 +40099,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -39586,7 +40237,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -39607,19 +40258,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -40138,7 +40792,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -40159,19 +40813,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -40245,7 +40902,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -40266,19 +40923,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -40687,7 +41347,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -40708,19 +41368,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -40794,7 +41457,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -40815,19 +41478,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -41011,7 +41677,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -41032,19 +41698,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -41207,7 +41876,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -41228,19 +41897,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -41560,7 +42232,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -41581,19 +42253,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -41984,7 +42659,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -42005,19 +42680,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -42429,7 +43107,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -42450,19 +43128,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -42770,7 +43451,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -42791,19 +43472,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -42966,7 +43650,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -42987,19 +43671,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -43319,7 +44006,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -43340,19 +44027,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -43743,7 +44433,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -43764,19 +44454,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -44188,7 +44881,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -44209,19 +44902,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -44543,7 +45239,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -44564,19 +45260,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -44739,7 +45438,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -44760,19 +45459,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -45092,7 +45794,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -45113,19 +45815,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -45516,7 +46221,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -45537,19 +46242,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -45961,7 +46669,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -45982,19 +46690,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -46303,7 +47014,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -46324,19 +47035,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -46499,7 +47213,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -46520,19 +47234,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -46852,7 +47569,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -46873,19 +47590,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -47276,7 +47996,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -47297,19 +48017,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -47721,7 +48444,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -47742,19 +48465,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -48014,7 +48740,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -48035,19 +48761,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -48210,7 +48939,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -48231,19 +48960,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -48563,7 +49295,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -48584,19 +49316,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -48987,7 +49722,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -49008,19 +49743,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -49432,7 +50170,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -49453,19 +50191,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -49802,7 +50543,7 @@ { "name": "run_on_provider", "title": "Run on a provider (can spend the person's money)", - "description": "Runs a program on one of the person's own quantum providers: their device, their account or their simulator. THIS CAN SPEND THE PERSON'S MONEY OR QUOTA. A run that is not free, or whose cost is unknown, is refused unless confirm_cost is true; set it only after the person has seen the estimate (provider_estimate) and agreed to that run. The answer is a job in the service's form: counts keyed by the classical bits, measured on the device and not reproducible by recomputation, with the circuit's exact distribution beside them, sealed in a receipt with the compiled program's hash, the provider, its protocol and the device's calibration snapshot (verify_quantum_result checks it). A device's queue can be long: if the job has not finished within this call the answer has status running and an id, nothing is cancelled and nothing is held, and get_provider_job collects it later, from any session, for as long as the provider keeps it. Do not run it again to find out: that is a second job, and it can be a second bill.", + "description": "Runs a program on one of the person's own quantum providers: their device, their account or their simulator. THIS CAN SPEND THE PERSON'S MONEY OR QUOTA. A run that is not free, or whose cost is unknown, is refused unless confirm_cost is true; set it only after the person has seen the estimate (provider_estimate) and agreed to that run. The answer is a job in the service's form: counts keyed by the classical bits, labelled measured from a processor and simulated from a simulator, not reproducible by recomputation, with where they come from (counts_source), what was set off at the provider (execution) and the circuit's exact distribution beside them, sealed in a receipt with the compiled program's text and hash, the provider, its protocol and the device's calibration snapshot (verify_quantum_result checks it). A device's queue can be long: if the job has not finished within this call the answer has status running and an id, nothing is cancelled and nothing is held, and get_provider_job collects it later, from any session, for as long as the provider keeps it. Do not run it again to find out: that is a second job, and it can be a second bill.", "inputSchema": { "type": "object", "required": [ @@ -50769,9 +51510,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -51125,7 +51871,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -51140,6 +51886,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -52555,7 +53313,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -52576,24 +53334,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -52608,17 +53369,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -52628,6 +53392,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -53461,7 +54334,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -53482,19 +54355,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -54397,9 +55273,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -54753,7 +55634,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -54768,6 +55649,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -56183,7 +57076,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -56204,24 +57097,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -56236,17 +57132,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -56256,6 +57155,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", diff --git a/wai/wai-quantum-cli/docs/service-mcp.json b/wai/wai-quantum-cli/docs/service-mcp.json index 6675ff071..d58babf6e 100644 --- a/wai/wai-quantum-cli/docs/service-mcp.json +++ b/wai/wai-quantum-cli/docs/service-mcp.json @@ -1475,9 +1475,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -1831,7 +1836,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -1846,6 +1851,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -3261,7 +3278,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -3282,24 +3299,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -3314,17 +3334,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -3334,6 +3357,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -4167,7 +4299,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -4188,19 +4320,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -5103,9 +5238,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -5459,7 +5599,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -5474,6 +5614,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -6889,7 +7041,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -6910,24 +7062,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -6942,17 +7097,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -6962,6 +7120,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -11304,9 +11571,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -11660,7 +11932,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -11675,6 +11947,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -13090,7 +13374,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -13111,24 +13395,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -13143,17 +13430,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -13163,6 +13453,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -13996,7 +14395,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -14017,19 +14416,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -14932,9 +15334,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -15288,7 +15695,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -15303,6 +15710,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -16718,7 +17137,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -16739,24 +17158,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -16771,17 +17193,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -16791,6 +17216,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -22153,9 +22687,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -22509,7 +23048,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -22524,6 +23063,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -23939,7 +24490,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -23960,24 +24511,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -23992,17 +24546,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -24012,6 +24569,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -24845,7 +25511,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -24866,19 +25532,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -25781,9 +26450,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -26137,7 +26811,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -26152,6 +26826,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -27567,7 +28253,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -27588,24 +28274,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -27620,17 +28309,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -27640,6 +28332,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -30519,14 +31320,14 @@ { "name": "verify_quantum_receipt", "title": "Verify a result's receipt", - "description": "Whether a result's receipt (an Ed25519 signature by the service) seals exactly that request and that result. Pass the job's own `request` (as run_quantum_circuit or get_quantum_job returns it: a run here asks for its verdict, so the request it seals says so) and its result.", + "description": "Whether a result's receipt (an Ed25519 signature) seals exactly that request and that result, and every rule of its form holds (`reasons` names what does not). Pass the job's own `request` (as run_quantum_circuit or get_quantum_job returns it: a run here asks for its verdict, so the request it seals says so) and its result.", "inputSchema": { "type": "object", - "required": [ - "request", - "result" - ], "properties": { + "job": { + "type": "string", + "description": "The job's text as it was given (preferred: read as the protocol reads it, so a member named twice or a number past what a parsed job keeps is seen)" + }, "request": { "type": "object" }, @@ -30548,7 +31349,7 @@ "anyOf": [ { "type": "object", - "description": "Whether a result's receipt seals exactly that request and that result, and the key it names.", + "description": "Whether a result's receipt seals exactly that request and that result and every rule of its form holds, and the key it names.", "required": [ "valid" ], @@ -30556,11 +31357,36 @@ "valid": { "type": "boolean" }, + "reasons": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Each rule the receipt breaks (the provider protocol's section 8.4): none when it is valid." + }, + "form": { + "type": [ + "string", + "null" + ], + "description": "The receipt's form: rfc8785, or first." + }, + "provider": { + "type": "object", + "description": "For a provider's result, what it says of itself." + }, "key": { "type": [ "string", "null" ] + }, + "signer": { + "type": [ + "string", + "null" + ], + "description": "That key as a did:key." } } }, @@ -30639,6 +31465,26 @@ "string", "null" ] + }, + "signer": { + "type": [ + "string", + "null" + ], + "description": "That key as a did:key, where a surface gives it." + }, + "reasons": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Each rule the set's receipt breaks (the provider protocol's sections 8.4 and 8.6): none when it is valid." + }, + "form": { + "type": [ + "string", + "null" + ] } } } @@ -38305,7 +39151,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -38326,19 +39172,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -40165,7 +41014,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -40186,19 +41035,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -44567,9 +45419,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -44923,7 +45780,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -44938,6 +45795,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -46353,7 +47222,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -46374,24 +47243,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -46406,17 +47278,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -46426,6 +47301,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -47259,7 +48243,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -47280,19 +48264,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -48195,9 +49182,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" @@ -48551,7 +49543,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -48566,6 +49558,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -49981,7 +50985,7 @@ "anyOf": [ { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -50002,24 +51006,27 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": [ "alg", @@ -50034,17 +51041,20 @@ "key": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "digest": { "type": "string", "minLength": 64, - "maxLength": 64 + "maxLength": 64, + "pattern": "^[0-9a-f]{64}$" }, "signature": { "type": "string", "minLength": 128, - "maxLength": 128 + "maxLength": 128, + "pattern": "^[0-9a-f]{128}$" } } } @@ -50054,6 +51064,115 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "twirling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "decoupling": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "remapping": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "optimisation": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -52235,7 +53354,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -52256,19 +53375,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -52390,7 +53512,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -52411,19 +53533,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -52546,7 +53671,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -52567,19 +53692,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -52988,7 +54116,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -53009,19 +54137,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -53143,7 +54274,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -53164,19 +54295,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -53299,7 +54433,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -53320,19 +54454,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -53695,7 +54832,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -53716,19 +54853,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -53850,7 +54990,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -53871,19 +55011,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -54006,7 +55149,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -54027,19 +55170,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -54558,7 +55704,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -54579,19 +55725,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -54665,7 +55814,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -54686,19 +55835,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -55107,7 +56259,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -55128,19 +56280,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -55214,7 +56369,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -55235,19 +56390,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } } @@ -55431,7 +56589,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -55452,19 +56610,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -55627,7 +56788,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -55648,19 +56809,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -55980,7 +57144,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -56001,19 +57165,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -56404,7 +57571,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -56425,19 +57592,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -56849,7 +58019,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -56870,19 +58040,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -57190,7 +58363,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -57211,19 +58384,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -57386,7 +58562,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -57407,19 +58583,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -57739,7 +58918,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -57760,19 +58939,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -58163,7 +59345,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -58184,19 +59366,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -58608,7 +59793,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -58629,19 +59814,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -58963,7 +60151,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -58984,19 +60172,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -59159,7 +60350,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -59180,19 +60371,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -59512,7 +60706,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -59533,19 +60727,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -59936,7 +61133,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -59957,19 +61154,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -60381,7 +61581,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -60402,19 +61602,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -60723,7 +61926,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -60744,19 +61947,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -60919,7 +62125,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -60940,19 +62146,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -61272,7 +62481,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -61293,19 +62502,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -61696,7 +62908,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -61717,19 +62929,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -62141,7 +63356,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -62162,19 +63377,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -62434,7 +63652,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -62455,19 +63673,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -62630,7 +63851,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -62651,19 +63872,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -62983,7 +64207,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -63004,19 +64228,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -63407,7 +64634,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -63428,19 +64655,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, @@ -63852,7 +65082,7 @@ }, "receipt": { "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": [ "alg", @@ -63873,19 +65103,22 @@ "type": "string", "minLength": 64, "maxLength": 64, - "description": "The signing key, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The signing key, 32 bytes in lower-case hex." }, "digest": { "type": "string", "minLength": 64, "maxLength": 64, - "description": "The digest signed, 32 bytes in hex." + "pattern": "^[0-9a-f]{64}$", + "description": "The digest signed, 32 bytes in lower-case hex." }, "signature": { "type": "string", "minLength": 128, "maxLength": 128, - "description": "The signature, 64 bytes in hex." + "pattern": "^[0-9a-f]{128}$", + "description": "The signature, 64 bytes in lower-case hex." } } }, diff --git a/wai/wai-quantum-cli/schemas/local-tools.v1.json b/wai/wai-quantum-cli/schemas/local-tools.v1.json index 63cb70dcc..7ea23dbf6 100644 --- a/wai/wai-quantum-cli/schemas/local-tools.v1.json +++ b/wai/wai-quantum-cli/schemas/local-tools.v1.json @@ -9,7 +9,7 @@ "anyOf": [ { "type": "object", - "description": "Whether a job's receipt seals its request and its result, and the key that signed it.", + "description": "Whether a job's receipt seals its request and its result and every rule of its form holds, and the key that signed it.", "required": [ "valid", "key" @@ -18,15 +18,43 @@ "valid": { "type": "boolean" }, + "reasons": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Each rule the receipt breaks (the provider protocol's section 8.4): none when it is valid." + }, + "form": { + "type": [ + "string", + "null" + ], + "description": "The receipt's form: rfc8785, or first." + }, + "provider": { + "type": "object", + "description": "For a provider's result, what it says of itself." + }, "key": { "type": [ "string", "null" ], "description": "The signing key, 32 bytes in hex." + }, + "signer": { + "type": [ + "string", + "null" + ], + "description": "That key as a did:key." } } }, + { + "$ref": "https://git.transaction.science/Transaction-Science/open-standards/raw/branch/main/wai/wai-quantum/schemas/set.v1.json#/$defs/verify" + }, { "$ref": "https://git.transaction.science/Transaction-Science/open-standards/raw/branch/main/wai/wai-quantum/schemas/estimate.v1.json#/$defs/verify" }, diff --git a/wai/wai-quantum-cli/src/main.rs b/wai/wai-quantum-cli/src/main.rs index 1e03e7ee4..444c3c78b 100644 --- a/wai/wai-quantum-cli/src/main.rs +++ b/wai/wai-quantum-cli/src/main.rs @@ -309,6 +309,12 @@ enum Command { /// Also check that the job carries this history's account of its program (`run --history`) #[arg(long, value_name = "FILE")] history: Option, + /// A job's verdict as JSON: valid, the reasons (wai/extensions/quantum-providers.md section 8.4), the key, the receipt's form, and what a provider's result says of itself + #[arg(long)] + json: bool, + /// One job a line, each judged as a job's receipt, one JSON verdict a line; the status is 1 when any is not valid + #[arg(long, conflicts_with = "history")] + lines: bool, }, /// Show the key this user signs receipts with Key { @@ -1385,35 +1391,108 @@ fn main() { } #[cfg(feature = "service")] Command::Task { cmd, args } => task::main(cmd, &args), - Command::Verify { file, history: hist } => { - let src = read_text(file.as_deref()).unwrap_or_else(|e| die(&e)); - let v: Value = serde_json::from_str(&src).unwrap_or_else(|e| die(&format!("not JSON: {e}"))); - if v["result"]["format"] == wai_quantum::engine::estimate::FORMAT { - if !estimate::verify(&v) { + Command::Verify { file, history: hist, json: as_json, lines } => { + let bytes = read_bytes(file.as_deref()).unwrap_or_else(|e| die(&e)); + let src = match String::from_utf8(bytes) { + Ok(s) => s, + Err(e) => { + // Not UTF-8: unreadable, as a verifier reads a job (section 8.2). + if as_json || lines { + println!("{}", job::check_bytes(e.as_bytes()).to_json()); + } else { + println!("signature INVALID (unreadable: the text is not UTF-8)"); + } std::process::exit(1); } - } else if v["result"]["format"] == wai_quantum::engine::sets::FORMAT { - let c = wai_quantum::engine::sets::verify(&v); - let yes = |b: &Value| if *b == true { "yes" } else { "NO" }; - println!("signature {}", if c["valid"] == true { "VALID" } else { "INVALID" }); - println!("binds the set's request and head, its root over {} items", c["items"]); - println!("items as the request gives them: {}; each sealed by its own receipt and its path: {}; the root: {}", yes(&c["derived"]), yes(&c["items_sealed"]), yes(&c["root"])); - if let Some(k) = c["key"].as_str() { - println!("signer {}", job::did_of(k).unwrap_or_else(|| k.to_string())); + }; + if lines { + let mut all = true; + for line in src.lines().filter(|l| !l.trim().is_empty()) { + let c = job::check_text(line); + all &= c.valid(); + println!("{}", c.to_json()); } - if c["valid"] != true { + if !all { + std::process::exit(1); + } + return; + } + let Ok(v) = serde_json::from_str::(&src) else { + // Not JSON at all: a job's verdict says so, as every reader must. + let c = job::check_text(&src); + if as_json { + println!("{}", c.to_json()); + std::process::exit(1); + } + die("not JSON"); + }; + // A set, an estimate and an algorithm's answer each have a verifier of + // their own, over what was read: their text is read as every job's is. + let own_verifier = v["result"]["format"] == wai_quantum::engine::estimate::FORMAT || v["result"]["schema"] == wai_quantum::engine::schema::ALGORITHMS; + let set = v["result"]["format"] == wai_quantum::engine::sets::FORMAT; + if set && hist.is_some() { + die("--history checks a run against its history; this is a set, which has none to check"); + } + if own_verifier { + if hist.is_some() { + die("--history checks a run against its history; this is a set, an estimate or an algorithm's answer, which has none to check"); + } + if let Some(reason) = job::text_refusal(&src) { + if as_json { + println!("{}", json!({ "valid": false, "reasons": [reason] })); + } else { + println!("rules BROKEN: {reason}"); + } + std::process::exit(1); + } + if as_json { + let verdict = if v["result"]["format"] == wai_quantum::engine::estimate::FORMAT { + wai_quantum::engine::estimate::verify(&v).unwrap_or_else(|e| die(&e)) + } else { + wai_quantum::engine::variational::verify(&v) + }; + println!("{verdict}"); + if verdict["valid"] != true { + std::process::exit(1); + } + return; + } + } + if v["result"]["format"] == wai_quantum::engine::estimate::FORMAT { + if !estimate::verify(&v) { std::process::exit(1); } } else if v["result"]["schema"] == wai_quantum::engine::schema::ALGORITHMS { if !variational::verify(&v) { std::process::exit(1); } - } else if v.get("request").is_some() && v.get("result").is_some() { - let (mut ok, key) = job::verify(&v).unwrap_or_else(|e| die(&e)); - println!("signature {}", if ok { "VALID" } else { "INVALID" }); - println!("binds the request and the result as given"); - if let Some(k) = key.as_str() { - println!("signer {}", job::did_of(k).unwrap_or_else(|| k.to_string())); + } else if as_json || (v.get("request").is_some() && v.get("result").is_some()) { + let c = job::check_text(&src); + if as_json { + // A set's verdict also says what the engine checks past its receipt. + let mut said = if set { job::verdict_of_text(&src).unwrap_or_else(|_| c.to_json()) } else { c.to_json() }; + if let Some(p) = &hist { + let h = history::load(p).unwrap_or_else(|e| die(&e)); + let r = h.check_job(&v).unwrap_or_else(|e| die(&e)); + if r["ok"] != true { + said["valid"] = json!(false); + } + said["history"] = r; + } + println!("{said}"); + if said["valid"] != true { + std::process::exit(1); + } + return; + } + let mut ok = c.valid(); + print_check(&c); + if set { + let s = wai_quantum::engine::sets::verify(&v); + let yes = |b: &Value| if *b == true { "yes" } else { "NO" }; + println!("set its receipt over its request and head, its root over {} items", s["items"]); + println!("items as the request gives them (each program written out again here): {}; each sealed by its own receipt and its path: {}; the root: {}", yes(&s["derived"]), yes(&s["items_sealed"]), yes(&s["root"])); + ok &= s["derived"] == true; } if let Some(c) = v["request"].get("history") { let hands: Vec = c["gates"].as_object().into_iter().flatten().map(|(h, n)| format!("{n} {}", h.replace('_', " "))).collect(); @@ -1429,6 +1508,9 @@ fn main() { std::process::exit(1); } } else { + if hist.is_some() { + die("--history checks a run against its history; this is no run's receipt"); + } lab::verify_receipt(&src); } } @@ -1542,6 +1624,56 @@ fn main() { } } +/// A job's verdict for a person: the seal, what it binds, the signer and the +/// receipt's form, the rules, and what a provider's result says of itself. +fn print_check(c: &job::Check) { + use wai_quantum::engine::receipt::{BAD_KEY, BAD_SIGNATURE, DIGEST_MISMATCH, RECEIPT_FORM}; + let seal = [job::UNREADABLE, job::NOT_A_JOB, job::NO_RECEIPT, RECEIPT_FORM, DIGEST_MISMATCH, BAD_KEY, BAD_SIGNATURE]; + let broken: Vec<&str> = c.reasons.iter().copied().filter(|r| seal.contains(r)).collect(); + println!("signature {}", if broken.is_empty() { "VALID".to_string() } else { format!("INVALID ({})", broken.join(", ")) }); + println!("binds the request and the result as given"); + if let Some(k) = &c.key { + println!("signer {}", job::did_of(k).unwrap_or_else(|| k.clone())); + } + if c.form == Some("first") { + println!("form the first, before the canonical form: it verifies only as its signer wrote it"); + } + let rules: Vec<&str> = c.reasons.iter().copied().filter(|r| !seal.contains(r)).collect(); + if broken.is_empty() { + println!("rules {}", if rules.is_empty() { "every rule a verifier checks holds (wai/extensions/quantum-providers.md section 8.4)".to_string() } else { format!("BROKEN: {}", rules.join(", ")) }); + } + let Some(p) = &c.provider else { return }; + let s = |v: &Value| v.as_str().map(str::to_string).unwrap_or_else(|| v.to_string()); + let class = s(&p["class"]); + let read = match (p["read_as"].as_str(), class.as_str()) { + (Some(r), c) if r == c => format!("{c}{}", if p["approximate"] == true { ", approximate, as its provider states" } else { "" }), + (Some(r), c) => format!("{c}, read as {r}: {}", s(&p["note"])), + (None, c) => format!("{c} (its provenance names no device or simulator)"), + }; + println!("accuracy {read}"); + if p["form"] == "earlier" { + println!("stated neither where its counts come from nor what was set at the provider: a result of the earlier form"); + return; + } + let source = match p["counts_source"].as_str() { + Some("shots") => "tallied from the device's records, one a shot", + Some("vendor_counts") => "the provider's own counts, as it returned them", + Some("probabilities") => "rebuilt from the probabilities the device gave: derived, not shot records", + _ => "not stated in a form a verifier reads", + }; + // A built-in client tallied or rebuilt them itself. On the open protocol the + // host received counts, and this is the provider's word for how it made them. + println!("counts {source}{}", if matches!(p["kind"].as_str(), Some("http" | "process")) { ", in the provider's word" } else { "" }); + let e = &p["execution"]; + let steps: Vec = wai_quantum::engine::provider::ASPECTS.iter().map(|k| format!("{k} {}", s(&e[*k]))).collect(); + let verbatim = match e["verbatim"].as_bool() { + Some(true) => "; the gates to run as written", + Some(false) => "", + None => "; whether they ran as written is not stated", + }; + println!("execution stated by the {}: {}{verbatim}", s(&e["stated_by"]), steps.join(", ")); +} + #[cfg(test)] mod tests { use super::*; diff --git a/wai/wai-quantum-cli/src/mcp.rs b/wai/wai-quantum-cli/src/mcp.rs index d685c6dbb..ba5264654 100644 --- a/wai/wai-quantum-cli/src/mcp.rs +++ b/wai/wai-quantum-cli/src/mcp.rs @@ -352,8 +352,8 @@ fn core_tools() -> Value { { "name": "verify_quantum_result", "title": "Check a result's receipt", - "description": "Whether a job's receipt seals its request and its result, as run_quantum_circuit or the job service returns them, and which key signed it.", - "inputSchema": { "type": "object", "required": ["job"], "properties": { "job": { "type": "object", "description": "The job: {\"request\": ..., \"result\": ...}, the result carrying its receipt." } } }, + "description": "Whether a job's receipt seals its request and its result, as run_quantum_circuit or the job service returns them, and every rule of its form holds (`reasons` names what does not), and which key signed it.", + "inputSchema": { "type": "object", "required": ["job"], "properties": { "job": { "type": ["object", "string"], "description": "The job's text as it was given (preferred: read as the protocol reads it, so a member named twice or a number past what a parsed job keeps is seen), or the job: {\"request\": ..., \"result\": ...}, the result carrying its receipt." } } }, }, { "name": "discover_evaluate", @@ -621,15 +621,27 @@ fn call(name: &str, args: &Value, memory_gb: u64, key: &SigningKey, client: &Cli } } "verify_quantum_result" => { - let given = args.get("job").ok_or("give \"job\": {\"request\": ..., \"result\": ...}")?; + let given = args.get("job").ok_or("give \"job\": {\"request\": ..., \"result\": ...}, or the job's text")?; + // Its text, read as the protocol's section 8.2 reads it: what a parsed + // job no longer shows (a member named twice, a number past what it keeps) is seen. + if let Some(text) = given.as_str() { + // Text that does not read is a verdict (unreadable), not a failure of the tool. + let parsed: Value = serde_json::from_str(text).unwrap_or(Value::Null); + if parsed["result"]["format"] == wai_quantum::engine::estimate::FORMAT || parsed["result"]["schema"] == wai_quantum::engine::schema::ALGORITHMS { + if let Some(reason) = job::text_refusal(text) { + return Ok(json!({ "valid": false, "reasons": [reason] })); + } + return if parsed["result"]["format"] == wai_quantum::engine::estimate::FORMAT { wai_quantum::engine::estimate::verify(&parsed) } else { Ok(wai_quantum::engine::variational::verify(&parsed)) }; + } + return job::verdict_of_text(text).or_else(|_| Ok(job::verdict_of(&job::check_text(text)))); + } if given["result"]["format"] == wai_quantum::engine::estimate::FORMAT { return wai_quantum::engine::estimate::verify(given); } if given["result"]["schema"] == wai_quantum::engine::schema::ALGORITHMS { return Ok(wai_quantum::engine::variational::verify(given)); } - let (valid, key) = job::verify(given)?; - Ok(json!({ "valid": valid, "key": key })) + job::verdict(given) } "list_methods" => Ok(json!({ "methods": catalog::describe(memory_gb << 30) })), "list_fabric" => Ok(wai_quantum::engine::fabric::list(memory_gb << 30)), diff --git a/wai/wai-quantum-cli/src/project.rs b/wai/wai-quantum-cli/src/project.rs index 1ae745a04..a6e92ec8b 100644 --- a/wai/wai-quantum-cli/src/project.rs +++ b/wai/wai-quantum-cli/src/project.rs @@ -383,7 +383,8 @@ pub fn test(path: &Path, memory_gb: u64, key: &wai_quantum::ed25519_dalek::Signi }; let request = json!({ "check": "intent", "program": p.program, "qasm": p.qasm, "run": p.run, "intent": given, "run_receipt": body["result"]["receipt"]["digest"] }); let mut result = json!({ "holds": code == 0, "level": v["level"], "verdict": v }); - result["receipt"] = receipt::seal(key, &request, &result); + job::exact_request(&request)?; + receipt::seal_into(key, &request, &mut result); let record = json!({ "request": request, "result": result }); let (run_path, check_path) = receipt_paths(&p.dir, &p.program); if let Some(d) = run_path.parent() { diff --git a/wai/wai-quantum-cli/src/providers.rs b/wai/wai-quantum-cli/src/providers.rs index 2637bd5bf..0ea7f2e0e 100644 --- a/wai/wai-quantum-cli/src/providers.rs +++ b/wai/wai-quantum-cli/src/providers.rs @@ -92,7 +92,7 @@ impl Books { /// `run`'s flags for a provider. #[derive(Args, Clone, Default)] pub struct RunArgs { - /// Run on one of your own providers (`wai-quantum providers list`) instead of here: a device answers counts, sealed as measured on it + /// Run on one of your own providers (`wai-quantum providers list`) instead of here: a device answers counts, sealed as measured on it, or as simulated when the provider's device is a simulator #[arg(long, value_name = "NAME")] pub provider: Option, /// With --provider: the plan (the exact compile to the device's gates and its check by simulation) and the provider's estimate; nothing is submitted @@ -112,10 +112,10 @@ pub enum Cmd { Add { /// Your name for it: a-z, 0-9, '.', '_' and '-' name: String, - /// sampler-v2, prx-circuits or native-turns (the built-in protocols), http or process (the open provider protocol) + /// sampler-v2, prx-circuits, native-turns or cloud-tasks (the built-in protocols), http or process (the open provider protocol) #[arg(long)] kind: String, - /// http: the provider's URL; a built-in kind: another endpoint than its own + /// http: the provider's URL; a built-in kind but cloud-tasks: another endpoint than its own #[arg(long)] endpoint: Option, /// A setting that is not a secret, as backend=NAME (repeatable) @@ -254,7 +254,7 @@ fn print_plan(p: &Value) { } else if e["free"] == true { "free".to_string() } else { - format!("{} {}{}", s(&e["amount"]), s(&e["currency"]), e.get("unit").map(|u| format!(" (billed by the {})", s(u))).unwrap_or_default()) + format!("{}{} {}{}", if e["at_least"] == true { "at least " } else { "" }, s(&e["amount"]), s(&e["currency"]), e.get("unit").map(|u| format!(" (billed by the {})", s(u))).unwrap_or_default()) }; let secs = e.get("seconds").and_then(Value::as_f64).map(|t| format!(", about {t} s")).unwrap_or_default(); println!("estimate {estimate}{secs}"); @@ -276,7 +276,7 @@ fn print_measured(body: &Value) { } let c = &r["cost"]; let charged = c["charged"].as_object().map(|o| format!("; charged {} {}", s(&o["amount"]), s(&o["currency"]))).unwrap_or_default(); - println!("cost {}{charged}", if c["free"] == true { "free".to_string() } else { format!("not free, confirmed{}", c["estimate"].get("amount").map(|a| format!("; estimated {} {}", s(a), s(&c["estimate"]["currency"]))).unwrap_or_default()) }); + println!("cost {}{charged}", if c["free"] == true { "free".to_string() } else { format!("not free, confirmed{}", c["estimate"].get("amount").map(|a| format!("; estimated {}{} {}", if c["estimate"]["at_least"] == true { "at least " } else { "" }, s(a), s(&c["estimate"]["currency"]))).unwrap_or_default()) }); } /// A sleep that says once, on standard error, that it is waiting. @@ -480,7 +480,7 @@ pub fn main(cmd: Cmd) { std::process::exit(if progress.job()["status"] == "cancelled" { 0 } else { status }); } Cmd::Add { name, kind, endpoint, settings, credential_stdin, credential_env, replace, command } => { - let kind = Kind::parse(&kind).unwrap_or_else(|| die(&format!("--kind {kind}: one of {}", Kind::ALL.map(Kind::name).join(", ")))); + let kind = Kind::parse(&kind).unwrap_or_else(|| die(&format!("--kind {kind}: one of {}", Kind::ALL.iter().map(|k| k.name()).collect::>().join(", ")))); let (mut reg, stores) = load(); let mut e = Entry { name, kind, endpoint, command, settings: settings.into_iter().collect::>(), credential: Credential::None }; if let Some(var) = credential_env { @@ -515,6 +515,9 @@ pub fn main(cmd: Cmd) { } Cmd::List { json } => { let (reg, _) = load(); + for n in reg.notes() { + eprintln!("note: {n}"); + } let list: Vec = reg.entries().iter().map(Entry::listing).collect(); if json { println!("{}", json!({ "registry": reg.path().display().to_string(), "providers": list })); @@ -639,7 +642,7 @@ pub fn mcp_tools() -> Vec { json!({ "name": "run_on_provider", "title": "Run on a provider (can spend the person's money)", - "description": "Runs a program on one of the person's own quantum providers: their device, their account or their simulator. THIS CAN SPEND THE PERSON'S MONEY OR QUOTA. A run that is not free, or whose cost is unknown, is refused unless confirm_cost is true; set it only after the person has seen the estimate (provider_estimate) and agreed to that run. The answer is a job in the service's form: counts keyed by the classical bits, measured on the device and not reproducible by recomputation, with the circuit's exact distribution beside them, sealed in a receipt with the compiled program's hash, the provider, its protocol and the device's calibration snapshot (verify_quantum_result checks it). A device's queue can be long: if the job has not finished within this call the answer has status running and an id, nothing is cancelled and nothing is held, and get_provider_job collects it later, from any session, for as long as the provider keeps it. Do not run it again to find out: that is a second job, and it can be a second bill.", + "description": "Runs a program on one of the person's own quantum providers: their device, their account or their simulator. THIS CAN SPEND THE PERSON'S MONEY OR QUOTA. A run that is not free, or whose cost is unknown, is refused unless confirm_cost is true; set it only after the person has seen the estimate (provider_estimate) and agreed to that run. The answer is a job in the service's form: counts keyed by the classical bits, labelled measured from a processor and simulated from a simulator, not reproducible by recomputation, with where they come from (counts_source), what was set off at the provider (execution) and the circuit's exact distribution beside them, sealed in a receipt with the compiled program's text and hash, the provider, its protocol and the device's calibration snapshot (verify_quantum_result checks it). A device's queue can be long: if the job has not finished within this call the answer has status running and an id, nothing is cancelled and nothing is held, and get_provider_job collects it later, from any session, for as long as the provider keeps it. Do not run it again to find out: that is a second job, and it can be a second bill.", "inputSchema": provider_schema(json!({ "confirm_cost": { "type": "boolean", "description": "true only when the person has agreed to this run's cost. A run that is not free is refused without it." } })), "execution": { "taskSupport": "optional" }, }), diff --git a/wai/wai-quantum-cli/tests/providers.rs b/wai/wai-quantum-cli/tests/providers.rs index aba60057a..e4cd391a3 100644 --- a/wai/wai-quantum-cli/tests/providers.rs +++ b/wai/wai-quantum-cli/tests/providers.rs @@ -125,15 +125,19 @@ fn a_person_adds_tests_plans_and_runs_on_a_provider_and_the_receipt_verifies() { // The run, sealed with this user's key, and checked by verify. let body = h.ok(&["run", &prog, "--shots", "300", "--seed", "4", "--provider", "lab", "--json"], ""); let job: Value = serde_json::from_str(&body).unwrap(); - assert_eq!((job["method"].as_str(), job["result"]["accuracy"]["class"].as_str()), (Some("provider"), Some("measured"))); + // The reference is a simulator: its counts are simulated, never measured, + // and the result says where they come from and how they ran. + assert_eq!((job["method"].as_str(), job["result"]["accuracy"]["class"].as_str(), job["result"]["counts_source"].as_str()), (Some("provider"), Some("simulated"), Some("shots"))); assert_eq!(job["request"]["provider"], "lab"); let saved = h.dir.join("job.json"); std::fs::write(&saved, &body).unwrap(); let check = h.ok(&["verify", saved.to_str().unwrap()], ""); - assert!(check.contains("signature VALID"), "{check}"); + assert!(check.contains("signature VALID") && check.contains("rules every rule") && check.contains("accuracy simulated") && check.contains("execution stated by the provider"), "{check}"); + let verdict: Value = serde_json::from_str(&h.ok(&["verify", "--json", saved.to_str().unwrap()], "")).unwrap(); + assert_eq!((verdict["valid"].as_bool(), verdict["provider"]["form"].as_str(), verdict["provider"]["counts_source"].as_str()), (Some(true), Some("stated"), Some("shots")), "{verdict}"); // As a person reads it. let text = h.ok(&["run", &prog, "--provider", "lab"], ""); - assert!(text.contains("accuracy measured") && text.contains("ideal total variation distance") && text.contains("cost free") && text.contains("receipt verifies"), "{text}"); + assert!(text.contains("accuracy simulated") && text.contains("ideal total variation distance") && text.contains("cost free") && text.contains("receipt verifies"), "{text}"); // A job the engine's methods would take and a device cannot is refused. let out = h.run(&["run", &prog, "--provider", "lab", "-o", "Z0"], ""); assert!(!out.status.success() && String::from_utf8_lossy(&out.stderr).contains("counts")); @@ -583,3 +587,21 @@ fn an_agent_runs_on_a_provider_as_a_task_and_the_server_answers_while_it_waits() assert_eq!(m.call(3, "tasks/cancel", json!({ "taskId": kept }))["result"]["status"], "cancelled"); m.close(); } + +/// A provider that names where its keys are, and finds nothing there, never +/// signs with whatever account's keys the shell has. Nothing here is sent. +#[test] +fn a_named_credential_that_is_empty_never_falls_to_the_account_s_keys() { + let h = Home::new("ambient"); + h.ok(&["providers", "add", "t", "--kind", "cloud-tasks", "--set", "region=us-east-1", "--set", "device=arn:device", "--set", "bucket=my-results", "--credential-env", "WAI_TEST_NOTHING_HERE"], ""); + let mut c = h.cmd(); + let out = c.env("AWS_ACCESS_KEY_ID", "AKIDEXAMPLE").env("AWS_SECRET_ACCESS_KEY", "not-a-real-one").env_remove("WAI_TEST_NOTHING_HERE").args(["providers", "describe", "t"]).output().unwrap(); // allow-name: the account's own variables + let said = String::from_utf8_lossy(&out.stderr); + assert!(!out.status.success() && said.contains("$WAI_TEST_NOTHING_HERE") && said.contains("gives nothing"), "{said}"); + // An endpoint it would not use, and a name that is not one, are refused when added. + for bad in [&["--endpoint", "http://127.0.0.1:1"][..], &["--set", "bucket=evil.example/x"][..]] { + let mut args = vec!["providers", "add", "u", "--kind", "cloud-tasks", "--set", "region=us-east-1"]; + args.extend_from_slice(bad); + assert!(!h.run(&args, "").status.success(), "{bad:?}"); + } +} diff --git a/wai/wai-quantum-cli/tests/verify.rs b/wai/wai-quantum-cli/tests/verify.rs new file mode 100644 index 000000000..f393ce2ea --- /dev/null +++ b/wai/wai-quantum-cli/tests/verify.rs @@ -0,0 +1,181 @@ +//! `wai-quantum verify`, on every kind of job it takes, and the command line's +//! MCP tool: the same rules, the corpus's verdicts, and no check skipped by +//! asking for another format. + +use serde_json::{Value, json}; +use std::io::Write; +use std::path::PathBuf; +use std::process::{Command, Output, Stdio}; + +const BIN: &str = env!("CARGO_BIN_EXE_wai-quantum"); + +fn corpus() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/receipts") +} + +fn scratch(tag: &str) -> PathBuf { + let d = std::env::temp_dir().join(format!("wai-quantum-cli-verify-{tag}-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&d); + std::fs::create_dir_all(&d).unwrap(); + d +} + +fn verify(args: &[&str]) -> Output { + Command::new(BIN).arg("verify").args(args).env_remove("WAI_QUANTUM_KEY").output().unwrap() +} + +fn key() -> wai_quantum::ed25519_dalek::SigningKey { + wai_quantum::ed25519_dalek::SigningKey::from_bytes(&[42; 32]) +} + +/// The job's text with `"name": 7` written in `result` before its real members. +fn twice_named(body: &Value, name: &str) -> String { + let text = body.to_string(); + let at = text.find("\"result\":{").unwrap() + "\"result\":{".len(); + format!("{}\"{name}\":7,{}", &text[..at], &text[at..]) +} + +#[test] +fn json_output_does_not_skip_the_history_it_was_asked_to_check() { + let job = corpus().join("jobs/cli-run-statevector.json"); + let job = job.to_str().unwrap(); + let plain = verify(&[job, "--history", "/nonexistent/history.json"]); + assert!(!plain.status.success(), "{}", String::from_utf8_lossy(&plain.stdout)); + let as_json = verify(&["--json", job, "--history", "/nonexistent/history.json"]); + assert!(!as_json.status.success(), "the same check, whatever the format: {}", String::from_utf8_lossy(&as_json.stdout)); +} + +#[test] +fn a_member_named_twice_is_unreadable_on_every_kind_of_job() { + let dir = scratch("twice"); + let key = key(); + let estimate = wai_quantum::engine::estimate::sealed(&json!({ "qasm": "OPENQASM 3.0;\nqubit q;\nrz(0.3) q;\n", "budget": 1e-3 }), &key).unwrap(); + let set = wai_quantum::engine::sets::run(&json!({ "programs": ["OPENQASM 3.0; qubit q; x q;"], "shots": 8 }), 1 << 30, &key, json!({ "unmetered": "a test" })).unwrap(); + let req = json!({ "hamiltonian": ["Z0 Z1"], "ansatz": { "layers": 1, "qubits": 2 } }); + let algorithm = wai_quantum::engine::variational::call("vqe", &req).map(|a| wai_quantum::engine::variational::seal(&req, a, &key)).unwrap(); + for (what, body, name) in [("estimate", estimate, "format"), ("set", set, "format"), ("algorithm", algorithm, "schema")] { + let file = dir.join(format!("{what}.json")); + std::fs::write(&file, twice_named(&body, name)).unwrap(); + let file = file.to_str().unwrap(); + let out = verify(&["--json", file]); + assert!(!out.status.success(), "{what}: {}", String::from_utf8_lossy(&out.stdout)); + let said: Value = serde_json::from_slice(&out.stdout).unwrap_or_else(|e| panic!("{what}: {e}: {}", String::from_utf8_lossy(&out.stdout))); + assert_eq!(said["reasons"], json!(["unreadable"]), "{what}"); + let out = verify(&[file]); + assert!(!out.status.success() && String::from_utf8_lossy(&out.stdout).contains("unreadable"), "{what}: {}", String::from_utf8_lossy(&out.stdout)); + } + let _ = std::fs::remove_dir_all(&dir); +} + +#[test] +fn json_output_is_json_on_every_kind_of_job() { + let dir = scratch("json"); + let key = key(); + let estimate = wai_quantum::engine::estimate::sealed(&json!({ "qasm": "OPENQASM 3.0;\nqubit q;\nrz(0.3) q;\n", "budget": 1e-3 }), &key).unwrap(); + let set = wai_quantum::engine::sets::run(&json!({ "programs": ["OPENQASM 3.0; qubit q; x q;"], "shots": 8 }), 1 << 30, &key, json!({ "unmetered": "a test" })).unwrap(); + for (what, body) in [("estimate", estimate), ("set", set)] { + let file = dir.join(format!("{what}.json")); + std::fs::write(&file, body.to_string()).unwrap(); + let out = verify(&["--json", file.to_str().unwrap()]); + assert!(out.status.success(), "{what}: {}", String::from_utf8_lossy(&out.stderr)); + let said: Value = serde_json::from_slice(&out.stdout).unwrap_or_else(|e| panic!("{what}: {e}: {}", String::from_utf8_lossy(&out.stdout))); + assert_eq!(said["valid"], true, "{what}: {said}"); + // A history cannot be checked against one: it says so, and does not pass. + let out = verify(&[file.to_str().unwrap(), "--history", "/nonexistent/history.json"]); + assert!(!out.status.success(), "{what}"); + // The tool agrees: a set's receipt seals its head, and is read so. + assert_eq!(mcp("verify_quantum_result", json!({ "job": body }))["valid"], true, "{what}"); + } + let _ = std::fs::remove_dir_all(&dir); +} + +/// One request to the MCP server on standard input and output. +fn mcp(tool: &str, arguments: Value) -> Value { + let mut child = Command::new(BIN).arg("mcp").env_remove("WAI_QUANTUM_KEY").stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::null()).spawn().unwrap(); + let mut stdin = child.stdin.take().unwrap(); + writeln!(stdin, "{}", json!({ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "test", "version": "0" } } })).unwrap(); + writeln!(stdin, "{}", json!({ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": tool, "arguments": arguments } })).unwrap(); + drop(stdin); + let out = child.wait_with_output().unwrap(); + let text = String::from_utf8_lossy(&out.stdout); + let answer: Value = text.lines().filter_map(|l| serde_json::from_str::(l).ok()).find(|v| v["id"] == 2).unwrap_or_else(|| panic!("{text}")); + answer["result"]["structuredContent"].clone() +} + +/// What can be seen in a parsed job, the MCP tool finds: a job's text is not +/// what it is given, so a member named twice has gone by then. +#[test] +fn the_mcp_tool_gives_the_corpus_s_verdict_on_what_a_parsed_job_shows() { + let manifest: Value = serde_json::from_str(&std::fs::read_to_string(corpus().join("manifest.json")).unwrap()).unwrap(); + for e in manifest["entries"].as_array().unwrap() { + let name = e["file"].as_str().unwrap(); + let want: Vec<&str> = e["reasons"].as_array().unwrap().iter().filter_map(Value::as_str).collect(); + // Nesting as deep as a reader allows, which a message around it takes past that. + if want.contains(&"unreadable") || want.contains(&"not_a_job") || want.contains(&"no_receipt") || name.contains("depth") { + continue; + } + let Ok(job) = serde_json::from_slice::(&std::fs::read(corpus().join(name)).unwrap()) else { continue }; + // A literal past what a parsed number keeps is for the text's reader. + if want == ["inexact_number"] && !wai_quantum::engine::job::check(&job).reasons.contains(&"inexact_number") { + continue; + } + let said = mcp("verify_quantum_result", json!({ "job": job })); + assert_eq!(said["valid"].as_bool(), Some(want.is_empty()), "{name}: {said}"); + let mut got: Vec<&str> = said["reasons"].as_array().unwrap_or_else(|| panic!("{name}: {said}")).iter().filter_map(Value::as_str).collect(); + got.sort_unstable(); + assert_eq!(got, want, "{name}"); + } +} + +/// What `verify` prints for a provider's result says whose word it is, and +/// claims nothing it cannot read. +#[test] +fn what_verify_says_is_only_what_the_receipt_shows() { + let said = |name: &str| { + let out = verify(&[corpus().join("jobs").join(name).to_str().unwrap()]); + assert!(out.status.success(), "{name}: {}", String::from_utf8_lossy(&out.stdout)); + String::from_utf8_lossy(&out.stdout).to_string() + }; + // On the open protocol the host received counts: it is the provider's word. + assert!(said("cli-provider-reference.json").contains("in the provider's word")); + // A built-in client tallied them itself. + assert!(!said("scripted-cloud-tasks-processor.json").contains("in the provider's word")); + // A form, and no time: nothing sealed says when. + let earlier = said("fixture-provider-job-legacy.json"); + assert!(earlier.contains("a result of the earlier form") && !earlier.contains("sealed before") && !earlier.contains("sealed as measured before"), "{earlier}"); +} + +/// `--lines` judges each line by its kind: a genuine set is valid. +#[test] +fn lines_judge_a_set_as_a_set() { + let dir = scratch("lines-set"); + let key = key(); + let set = wai_quantum::engine::sets::run(&json!({ "programs": ["OPENQASM 3.0; qubit q; x q;"], "shots": 8 }), 1 << 30, &key, json!({ "unmetered": "a test" })).unwrap(); + let job = std::fs::read_to_string(corpus().join("jobs/cli-run-statevector.json")).unwrap(); + let file = dir.join("lines.jsonl"); + std::fs::write(&file, format!("{set}\n{}\n", job.trim())).unwrap(); + let out = verify(&["--lines", file.to_str().unwrap()]); + let said: Vec = String::from_utf8_lossy(&out.stdout).lines().map(|l| serde_json::from_str(l).unwrap()).collect(); + assert_eq!(said.len(), 2); + assert!(said.iter().all(|v| v["valid"] == true), "{said:?}"); + assert!(out.status.success()); + let _ = std::fs::remove_dir_all(&dir); +} + +/// The tool given a job's text reads it as §8.2 does: every receipt of the +/// corpus gets the manifest's reasons, none skipped. +#[test] +fn the_mcp_tool_given_a_job_s_text_gives_the_corpus_s_verdict() { + let manifest: Value = serde_json::from_str(&std::fs::read_to_string(corpus().join("manifest.json")).unwrap()).unwrap(); + for e in manifest["entries"].as_array().unwrap() { + let name = e["file"].as_str().unwrap(); + let mut want: Vec<&str> = e["reasons"].as_array().unwrap().iter().filter_map(Value::as_str).collect(); + want.sort_unstable(); + // A tool is given a string, which holds UTF-8 only: bytes that are not are a file's, and its reader's. + let Ok(text) = String::from_utf8(std::fs::read(corpus().join(name)).unwrap()) else { continue }; + let said = mcp("verify_quantum_result", json!({ "job": text })); + let mut got: Vec<&str> = said["reasons"].as_array().unwrap_or_else(|| panic!("{name}: {said}")).iter().filter_map(Value::as_str).collect(); + got.sort_unstable(); + assert_eq!(got, want, "{name}"); + } +} diff --git a/wai/wai-quantum-desktop/Cargo.lock b/wai/wai-quantum-desktop/Cargo.lock index c8d233b8e..aaa0bcf9d 100644 --- a/wai/wai-quantum-desktop/Cargo.lock +++ b/wai/wai-quantum-desktop/Cargo.lock @@ -4500,10 +4500,11 @@ dependencies = [ [[package]] name = "wai-quantum-qpu" -version = "0.4.1" +version = "0.5.0" dependencies = [ "blake3", "serde_json", + "sha2", "wai-quantum", ] diff --git a/wai/wai-quantum-desktop/Cargo.toml b/wai/wai-quantum-desktop/Cargo.toml index 4ea7d7131..0e638b3e5 100644 --- a/wai/wai-quantum-desktop/Cargo.toml +++ b/wai/wai-quantum-desktop/Cargo.toml @@ -19,7 +19,7 @@ wai-quantum = { version = "0.8.1", path = "../wai-quantum", default-features = f wai-quantum-cli = { version = "0.2.4", path = "../wai-quantum-cli", default-features = false } # The person's own providers: their registry, their credentials in the # operating system's keychain, and runs on them sealed with the app's key. -wai-quantum-qpu = { version = "0.4.1", path = "../wai-quantum-qpu" } +wai-quantum-qpu = { version = "0.5.0", path = "../wai-quantum-qpu" } # Updates: a newer release found in its update manifest, and installed only # when its signature verifies with the public key in tauri.conf.json. tauri-plugin-updater = "2.14.0" diff --git a/wai/wai-quantum-desktop/src/main.rs b/wai/wai-quantum-desktop/src/main.rs index 896b877ce..1bc4c8362 100644 --- a/wai/wai-quantum-desktop/src/main.rs +++ b/wai/wai-quantum-desktop/src/main.rs @@ -624,10 +624,7 @@ fn picture(qasm: String, format: String) -> String { #[tauri::command] fn verify(job: String) -> String { - match serde_json::from_str::(&job).map_err(|e| format!("not JSON: {e}")).and_then(|v| job::verify(&v)) { - Ok((valid, key)) => json!({ "valid": valid, "signer": key.as_str().and_then(job::did_of), "key": key }).to_string(), - Err(e) => error_json(e), - } + job::verdict_of_text(&job).map(|v| v.to_string()).unwrap_or_else(error_json) } #[tauri::command] @@ -1153,7 +1150,8 @@ mod tests { assert_eq!(refused["needs_confirmation"], true, "{refused}"); assert!(ledger.list().0.is_empty(), "a refused run is not recorded, since nothing was sent"); let body: Value = serde_json::from_str(&run_on(&t, &req, &key, true, &ledger, wait)).unwrap(); - assert_eq!(body["result"]["accuracy"]["class"], "measured", "{body}"); + // The reference is a simulator: its counts are simulated, never measured. + assert_eq!(body["result"]["accuracy"]["class"], "simulated", "{body}"); let (ok, k) = job::verify(&body).unwrap(); assert!(ok); assert_eq!(job::did_of(k.as_str().unwrap()).unwrap(), wai_quantum::quantum_receipt::did_key(&key.verifying_key())); @@ -1424,4 +1422,16 @@ mod tests { assert_eq!((log["check"]["ok"].as_bool(), log["check"]["sealed"].as_u64()), (Some(true), Some(2)), "{log}"); assert!(history_in(&key, "start", "not json").contains("not JSON")); } + + /// The app's verify reads a job's text as the provider protocol's section 8.2 + /// reads it, and holds every rule of section 8.4. + #[test] + fn verify_reads_a_job_s_text_and_holds_every_rule() { + let jobs = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/receipts/jobs"); + let said = |name: &str| -> Value { serde_json::from_str(&verify(std::fs::read_to_string(jobs.join(name)).unwrap())).unwrap() }; + assert_eq!(said("cli-run-statevector.json")["valid"], true); + assert_eq!(said("crafted-read-duplicate.json")["reasons"], json!(["unreadable"])); + assert_eq!(said("crafted-number-inexact.json")["reasons"], json!(["inexact_number"])); + assert_eq!(said("crafted-provider-measured-simulator.json")["reasons"], json!(["provider_class"])); + } } diff --git a/wai/wai-quantum-js/README.md b/wai/wai-quantum-js/README.md index ad9bcda1b..76d78cef7 100644 --- a/wai/wai-quantum-js/README.md +++ b/wai/wai-quantum-js/README.md @@ -21,12 +21,13 @@ import init, { QuantumEngine, engine_verify } from '@transaction-science/wai-qua await init(); const engine = new QuantumEngine(crypto.getRandomValues(new Uint8Array(32)), 256); -const job = JSON.parse(engine.run(JSON.stringify({ +const text = engine.run(JSON.stringify({ qasm: 'OPENQASM 3.0; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;', shots: 1000, -}))); +})); +const job = JSON.parse(text); console.log(job.method, job.result.counts); -console.log(JSON.parse(engine_verify(JSON.stringify(job))).valid); // true +console.log(JSON.parse(engine_verify(text)).valid); // true: the receipt checked on the text it came in ``` ## In Node @@ -42,8 +43,8 @@ import { randomBytes } from 'node:crypto'; const wasm = createRequire(import.meta.url).resolve('@transaction-science/wai-quantum/wai_quantum_bg.wasm'); await init({ module_or_path: await readFile(wasm) }); const engine = new QuantumEngine(randomBytes(32), 256); -const job = JSON.parse(engine.run(JSON.stringify({ qasm: 'OPENQASM 3.0; qubit[1] q; bit[1] c; h q[0]; c = measure q;', shots: 100 }))); -console.log(job.result.counts, JSON.parse(engine_verify(JSON.stringify(job))).signer); +const text = engine.run(JSON.stringify({ qasm: 'OPENQASM 3.0; qubit[1] q; bit[1] c; h q[0]; c = measure q;', shots: 100 })); +console.log(JSON.parse(text).result.counts, JSON.parse(engine_verify(text)).signer); ``` ## What it offers @@ -62,7 +63,7 @@ Requests and answers are JSON strings, the job service's own. A call that cannot **Functions:** -- `engine_verify(job)`: whether a job's receipt seals its request and result: `{"valid", "key", "signer"}`. Any change to either after sealing makes it false. +- `engine_verify(text)`: whether a job's receipt seals its request and result and every rule of its form holds (the provider protocol's section 8.4, and §8.6 for a set): `{"valid", "reasons", "key", "form", "signer"}`, each reason naming a rule broken. Give it the job's text as it came: an object parsed and written out again by JavaScript can differ from what was sealed (a member named by digits moves first, and a large whole number is rewritten), and a member named twice is gone once it is parsed. - `engine_verdict(job, intent, memory_mb)`: a job's verdict against its intent (or the one given). - `engine_estimate_verify(answer)`: an estimate's seal, its receipts, and its request re-run to the same figures. - `engine_lint(src)`: a program read as the engine reads it: `{"ok": true, "qubits", "gates", …}`, or `{"ok": false, "error", "line", "code"}`. diff --git a/wai/wai-quantum-py/notebooks/bell-receipt.ipynb b/wai/wai-quantum-py/notebooks/bell-receipt.ipynb index 935985a49..8e1f8f32f 100644 --- a/wai/wai-quantum-py/notebooks/bell-receipt.ipynb +++ b/wai/wai-quantum-py/notebooks/bell-receipt.ipynb @@ -190,7 +190,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'valid': True, 'key': '03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8', 'signer': 'did:key:z6MkehRgf7yJbgaGfYsdoAsKdBPE3dj2CYhowQdcjqSJgvVd'}\n" + "{'valid': True, 'reasons': [], 'key': '03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8', 'form': 'rfc8785', 'signer': 'did:key:z6MkehRgf7yJbgaGfYsdoAsKdBPE3dj2CYhowQdcjqSJgvVd'}\n" ] } ], diff --git a/wai/wai-quantum-py/python/wai_quantum/_api.py b/wai/wai-quantum-py/python/wai_quantum/_api.py index 855e078e7..db8520b9e 100644 --- a/wai/wai-quantum-py/python/wai_quantum/_api.py +++ b/wai/wai-quantum-py/python/wai_quantum/_api.py @@ -210,10 +210,19 @@ def _job_of(job: Any) -> Mapping[str, Any]: def verify(job: Any) -> Verification: - """Whether a job's receipt seals its request and its result, checked - here, offline, as every surface checks it: a job run here, by the - command line, by the service or in a browser tab. ``job`` is a dict - (``{"request": ..., "result": ...}``), JSON text, or a path.""" + """Whether a job's receipt seals its request and its result, and every + rule of the provider protocol's section 8.4 holds, checked here, offline, + as every surface checks it: a job run here, by the command line, by the + service or in a browser tab. ``job`` is a dict (``{"request": ..., + "result": ...}``), JSON text or its bytes, or a path. Text and a path are + read as the protocol reads them, so a member named twice is found, which a + dict can no longer show. ``reasons`` names what is wrong.""" + if isinstance(job, (bytes, bytearray)): + return Verification(_engine.verify_text(bytes(job))) + if isinstance(job, str) and job.lstrip().startswith("{"): + return Verification(_engine.verify_text(job.encode("utf-8"))) + if isinstance(job, (str, os.PathLike)): + return Verification(_engine.verify_text(Path(job).read_bytes())) return Verification(_engine.verify(_job_of(job))) diff --git a/wai/wai-quantum-py/python/wai_quantum/_engine.pyi b/wai/wai-quantum-py/python/wai_quantum/_engine.pyi index bb3359568..147e65a53 100644 --- a/wai/wai-quantum-py/python/wai_quantum/_engine.pyi +++ b/wai/wai-quantum-py/python/wai_quantum/_engine.pyi @@ -16,6 +16,7 @@ def improve(src: str) -> Dict[str, Any]: ... def shortcuts(src: str, thorough: bool = False) -> Dict[str, Any]: ... def equivalent(a: str, b: str) -> Dict[str, Any]: ... def verify(job_body: Any) -> Dict[str, Any]: ... +def verify_text(text: bytes) -> Dict[str, Any]: ... def verdict(job_body: Any, intent: Any = None, memory_gb: int = 16) -> Dict[str, Any]: ... def repair(src: str, options: Any) -> Dict[str, Any]: ... def seal(request: Any, result: Any, seed: bytes) -> Dict[str, Any]: ... diff --git a/wai/wai-quantum-py/python/wai_quantum/_job.py b/wai/wai-quantum-py/python/wai_quantum/_job.py index 65fd4b91d..6c0c31e60 100644 --- a/wai/wai-quantum-py/python/wai_quantum/_job.py +++ b/wai/wai-quantum-py/python/wai_quantum/_job.py @@ -9,14 +9,19 @@ __all__ = ["Job", "Verification"] class Verification(dict): - """Whether a receipt seals its request and its result: ``valid``, the - ``key`` it names (64 hex digits) and the ``signer`` (that key as a - ``did:key``). True exactly when the receipt is valid.""" + """Whether a receipt seals its request and its result and every rule + holds: ``valid``, the ``reasons`` when it is not, the ``key`` it names (64 + hex digits) and the ``signer`` (that key as a ``did:key``). True exactly + when the receipt is valid.""" @property def valid(self) -> bool: return self.get("valid") is True + @property + def reasons(self) -> List[str]: + return list(self.get("reasons") or []) + @property def signer(self) -> Optional[str]: return self.get("signer") diff --git a/wai/wai-quantum-py/src/lib.rs b/wai/wai-quantum-py/src/lib.rs index a2c3102a9..e898768d3 100644 --- a/wai/wai-quantum-py/src/lib.rs +++ b/wai/wai-quantum-py/src/lib.rs @@ -237,14 +237,22 @@ fn repair<'py>(py: Python<'py>, src: &str, options: &Bound<'py, PyAny>) -> PyRes to_py(py, &v) } -/// Whether a job's receipt seals its request and its result: `{"valid", -/// "key", "signer"}`, the signer as a `did:key`. +/// Whether a job's receipt seals its request and its result, and every rule of +/// the provider protocol's section 8.4 that a parsed job can show holds: +/// `{"valid", "reasons", "key", "form", "signer"}` (the signer as a `did:key`), +/// and what a provider's result says of itself. #[pyfunction] fn verify<'py>(py: Python<'py>, job_body: &Bound<'py, PyAny>) -> PyResult> { let v = to_value(job_body, 0)?; - let (valid, key) = job::verify(&v).map_err(|e| engine_error(py, e))?; - let signer = key.as_str().and_then(job::did_of); - to_py(py, &json!({ "valid": valid, "key": key, "signer": signer })) + to_py(py, &job::verdict(&v).map_err(|e| engine_error(py, e))?) +} + +/// [`verify`] of a job's text, read as section 8.2 reads it: a member named +/// twice and a number no double holds are seen here, and gone once it is parsed. +#[pyfunction] +fn verify_text<'py>(py: Python<'py>, text: &[u8]) -> PyResult> { + let verdict = std::str::from_utf8(text).ok().and_then(|t| job::verdict_of_text(t).ok()).unwrap_or_else(|| job::verdict_of(&job::check_bytes(text))); + to_py(py, &verdict) } /// What a fault-tolerant machine would need to run a program (or a @@ -366,6 +374,7 @@ fn _engine(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(improve, m)?)?; m.add_function(wrap_pyfunction!(shortcuts, m)?)?; m.add_function(wrap_pyfunction!(equivalent, m)?)?; + m.add_function(wrap_pyfunction!(verify_text, m)?)?; m.add_function(wrap_pyfunction!(verify, m)?)?; m.add_function(wrap_pyfunction!(verdict, m)?)?; m.add_function(wrap_pyfunction!(repair, m)?)?; diff --git a/wai/wai-quantum-py/tests/test_engine.py b/wai/wai-quantum-py/tests/test_engine.py index c0faf433a..3a09e2131 100644 --- a/wai/wai-quantum-py/tests/test_engine.py +++ b/wai/wai-quantum-py/tests/test_engine.py @@ -3,6 +3,7 @@ from __future__ import annotations import json +import pathlib import math import pickle import xml.etree.ElementTree as ET @@ -544,3 +545,38 @@ def test_a_mitigated_run_carries_each_value_raw_and_mitigated_with_its_assumptio assert wq.run(ghz, observables=["Z0 Z1"], noise=noise, mitigate="vd", key=DEMO)["result"]["mitigation"]["method"] == "vd" with pytest.raises(wq.EngineError, match=r"\[J008\]"): wq.run(ghz, observables=["Z0"], mitigate="zne", key=DEMO) + + +def test_every_receipt_of_the_corpus_gets_its_verdict_here(): + """The text of each job: read as the provider protocol's section 8.2 reads + it, and held to every rule of section 8.4, as the command line holds it.""" + corpus = pathlib.Path(__file__).resolve().parents[2] / "quantum-conformance" / "receipts" + manifest = json.loads((corpus / "manifest.json").read_text()) + for e in manifest["entries"]: + text = (corpus / e["file"]).read_bytes() + v = wq.verify(text) + assert sorted(v.reasons) == sorted(e["reasons"]), e["file"] + assert v.valid is (not e["reasons"]), e["file"] + + +def test_a_dict_is_held_to_every_rule_it_can_show(): + corpus = pathlib.Path(__file__).resolve().parents[2] / "quantum-conformance" / "receipts" + for name, reason in [("crafted-provider-measured-simulator", "provider_class"), ("crafted-number-inexact", "inexact_number")]: + job = json.loads((corpus / "jobs" / (name + ".json")).read_text()) + v = wq.verify(job) + assert not v.valid and reason in v.reasons, name + # Text and a path are read as text: a member named twice is found. + twice = corpus / "jobs" / "crafted-read-duplicate.json" + assert not wq.verify(str(twice)).valid + assert wq.verify(str(twice)).reasons == ["unreadable"] + assert wq.verify(twice.read_text()).reasons == ["unreadable"] + + +def test_a_set_s_text_is_judged_as_a_set(): + """A genuine set, given as text, a path or bytes, is valid.""" + corpus = pathlib.Path(__file__).resolve().parents[2] / "quantum-conformance" / "receipts" / "jobs" + for name in ("cli-set-programs.json", "cli-set-sweep.json"): + path = corpus / name + assert wq.verify(json.loads(path.read_text())).valid, name + assert wq.verify(str(path)).valid, (name, wq.verify(str(path))) + assert wq.verify(path.read_bytes()).valid, name diff --git a/wai/wai-quantum-qpu/Cargo.lock b/wai/wai-quantum-qpu/Cargo.lock index 77beb9ea6..d9a61b276 100644 --- a/wai/wai-quantum-qpu/Cargo.lock +++ b/wai/wai-quantum-qpu/Cargo.lock @@ -433,10 +433,11 @@ dependencies = [ [[package]] name = "wai-quantum-qpu" -version = "0.4.1" +version = "0.5.0" dependencies = [ "blake3", "serde_json", + "sha2", "wai-quantum", ] diff --git a/wai/wai-quantum-qpu/Cargo.toml b/wai/wai-quantum-qpu/Cargo.toml index a38b555cb..6ad446702 100644 --- a/wai/wai-quantum-qpu/Cargo.toml +++ b/wai/wai-quantum-qpu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wai-quantum-qpu" -version = "0.4.1" +version = "0.5.0" edition = "2024" license = "Apache-2.0" description = "Quantum processors as members of the wai-quantum fabric, and the person's own providers: clients for the services that run circuits on them, an open provider protocol (HTTP or a local process) with its reference provider and conformance checker, and a registry whose credentials stay in the operating system's keychain. Each circuit is compiled exactly to the processor's own gates and connectivity, checked against the circuit before it is sent, its cost shown before it is submitted, and its counts read back onto the circuit's qubits and sealed in a receipt." @@ -14,3 +14,5 @@ categories = ["science", "api-bindings"] wai-quantum = { version = "0.8.1", path = "../wai-quantum", default-features = false, features = ["quantum_fabric", "quantum_engine", "quantum_receipt"] } serde_json = "1" blake3 = "1" +# SHA-256 and its HMAC, which the cloud-tasks service's request signatures are made of. +sha2 = "0.10" diff --git a/wai/wai-quantum-qpu/README.md b/wai/wai-quantum-qpu/README.md index 30baf56c6..30d646ba0 100644 --- a/wai/wai-quantum-qpu/README.md +++ b/wai/wai-quantum-qpu/README.md @@ -19,6 +19,7 @@ A queue can outlast any wait, so `job::start` records a job before it is sent (` | `sampler_v2` | Sampler V2 jobs: an IAM bearer token, one OpenQASM 3 circuit per job | `rz`, `sx`, `x`, `cz` on physical qubits `$p` | radians | | `prx_circuits` | circuit jobs of native instructions on named qubits, one measurement key per classical bit | `prx`, `cz` | radians | | `native_turns` | circuit jobs in a native gateset that runs as written; a simulator with device noise models | `gpi`, `gpi2`, `zz` | turns, so every dyadic angle is exact | +| `cloud_tasks` | quantum tasks on your own cloud account: OpenQASM 3, a verbatim box on a processor, requests signed with your keys, results in your own bucket | a processor's `prx` `cz` or `gpi` `gpi2` `zz`; `rz` `v` `x` `cz` on a simulator | multiples of pi, written exactly | Each module's `BASE` constant is the endpoint it is written against. Each client is configured from the environment (`from_env`) or in code. Keys and tokens are held in `Secret`, which never prints: @@ -27,6 +28,7 @@ Each module's `BASE` constant is the endpoint it is written against. Each client | `sampler_v2` | `WAI_QPU_SAMPLER_API_KEY`, `WAI_QPU_SAMPLER_CRN`, `WAI_QPU_SAMPLER_BACKEND` | | `prx_circuits` | `WAI_QPU_PRX_TOKEN`, `WAI_QPU_PRX_QC` | | `native_turns` | `WAI_QPU_TURNS_KEY`, `WAI_QPU_TURNS_BACKEND` (default `simulator`), `WAI_QPU_TURNS_NOISE` | +| `cloud_tasks` | the account's usual key variables, `WAI_QPU_TASKS_REGION`, `WAI_QPU_TASKS_DEVICE`, `WAI_QPU_TASKS_BUCKET`, `WAI_QPU_TASKS_PREFIX` (default `wai-quantum`), `WAI_QPU_TASKS_BASIS` (a simulator only: compile to a processor's basis) | ```rust compile crate=wai-quantum-qpu use wai_quantum_qpu::{QpuSampler, check_probe, probe_circuit, transport::Curl}; @@ -48,7 +50,7 @@ A device needs no client written for it here: it speaks the [open provider proto | `reference` | the reference provider: the protocol around this crate's exact simulator, served over HTTP or standard input and output; the tests run against it, and it is the template to copy | | `conformance` | the checker, and its vectors in [`conformance/provider-v1.json`](conformance/provider-v1.json) with the reference's counts pinned | | `registry` | the person's list of providers (`~/.config/wai/quantum-providers.json`, holding no credential), and the credential stores: the keychain (the system `security` tool on macOS, `secret-tool` on Linux), else a file only its owner reads, labelled so | -| `job` | the dry run (the exact compile, the check by simulation, the provider's estimate and billing unit), the refusal of a run that is not free until it is confirmed, and the seal of its counts by the engine (`wai_quantum::engine::provider`), labelled `measured` | +| `job` | the dry run (the exact compile, the check by simulation, the provider's estimate and billing unit), the refusal of a run that is not free until it is confirmed, and the seal of its counts by the engine (`wai_quantum::engine::provider`), labelled `measured` from a processor and `simulated` from a simulator, with the counts' source and the `execution` each client states from the very request it sends | `Provider` gains `details` (the protocol, the calibration snapshot, the billing), `estimate`, a seeded `submit_seeded` and `cancel`, each with a default, so a client that has none keeps working: its cost is then unknown, and a run on it is never taken to be free. @@ -60,12 +62,12 @@ The command line carries it all: `wai-quantum providers add|list|remove|describe ```text norun=usage wai-qpu device # the device: qubits, basis, provenance -wai-qpu probe [--qubits N] [--shots N] # the bit-order probe +wai-qpu probe [--qubits N] [--shots N] [--yes] # the bit-order probe wai-qpu certify [--shots N] [--cases N] [--report FILE] [--yes] wai-qpu reference --stdio | --http ADDR [device options] # the reference provider ``` -`` is `sampler-v2`, `prx-circuits` or `native-turns`, configured from its variables above. A certification runs every circuit of the corpus in both gatesets as a job. On a device whose provenance is silicon that spends the service's quota, so it runs only with `--yes`. `--report` writes each certificate with its hash, and every job, as JSON. +`` is `sampler-v2`, `prx-circuits`, `native-turns` or `cloud-tasks`, configured from its variables above. A certification runs every circuit of the corpus in both gatesets as a job. On a device whose provenance is silicon, or one the service does not state is free, a job spends the service's quota or the person's money, so a probe and a certification there run only with `--yes`. `--report` writes each certificate with its hash, and every job, as JSON. ## Transport diff --git a/wai/wai-quantum-qpu/bring-your-own-quantum.md b/wai/wai-quantum-qpu/bring-your-own-quantum.md index 92de9f9fd..112e802bd 100644 --- a/wai/wai-quantum-qpu/bring-your-own-quantum.md +++ b/wai/wai-quantum-qpu/bring-your-own-quantum.md @@ -35,7 +35,7 @@ lists what anyone can use at no cost: ## Adding a provider -A provider is one of five kinds: +A provider is one of six kinds: | kind | what it is | the credential | |---|---|---| @@ -44,6 +44,7 @@ A provider is one of five kinds: | `sampler-v2` | a Sampler V2 job service (OpenQASM 3 on physical qubits, rz sx x cz) | an API key, exchanged for a bearer token | | `prx-circuits` | a circuit service of phased-X and CZ instructions | a token | | `native-turns` | a native-gate service with angles in turns (gpi gpi2 zz) | an API key | +| `cloud-tasks` | a cloud service of quantum tasks, on your own account, its results in your own storage bucket | your account's keys, `id:secret` or `id:secret:token`, or its usual environment variables | ```sh runnable # A program of your own, or the reference provider, on this machine: @@ -55,6 +56,9 @@ wai-quantum providers add cloud --kind http --endpoint https://quantum.example/p # A built-in protocol, its settings beside it, its credential in the keychain: wai-quantum providers add mine --kind sampler-v2 --set crn=… --set backend=… --credential-stdin < key.txt +# A cloud service of tasks: a device by its name there, your own results bucket and your own keys: +wai-quantum providers add tasks --kind cloud-tasks --set region=us-east-1 --set device=… --set bucket=your-results-bucket --credential-stdin < keys.txt + # Or a credential read from an environment variable whenever it is used, kept nowhere: wai-quantum providers add turns --kind native-turns --set backend=simulator --credential-env MY_TURNS_KEY ``` @@ -187,22 +191,48 @@ trusted. ## What the receipt proves, and what it does not -A provider's result is labelled `measured`. Its receipt binds the request, -the provider's name, kind and protocol, the device and its provenance, the -hash of the provider's own description, the calibration snapshot, the hash of -the compiled program, the counts, the cost (the estimate, the charge, and -whether you confirmed it), and the counts set beside the circuit's exact -distribution with the distance between them. +A processor's result is labelled `measured`. A result from a provider's +simulator or emulator is labelled `simulated`, since no device measured it, +and `approximate` too when the provider says its simulator is. Its receipt +binds the request, the provider's name, kind and protocol, the device and its +provenance, the hash of the provider's own description, the calibration +snapshot, the compiled program's text and its hash, the counts, the cost (the +estimate, the charge, and whether you confirmed it), and the counts set beside +the circuit's exact distribution with the distance between them. -It proves that this result is what your host received for this request from -the provider and device it names, for the program whose hash it carries, and -that your host compiled that program exactly and checked it against your -circuit before sending it. It does not prove that the device ran that -program, or ran anything: the counts are the provider's word. They are not +It also says two things a later receipt could not add: + +- **Where the counts come from** (`counts_source`): tallied from the device's + records, one a shot (`shots`); the provider's own counts (`vendor_counts`); + or rebuilt from probabilities (`probabilities`). Rebuilt counts are derived, + not shot records. `native-turns` always rebuilds them, since its service + answers probabilities; `cloud-tasks` reads a task's rows and rebuilds only + when its result has none. +- **What was set at the provider** (`execution`), built from the very request + the client sends: each of mitigation, twirling, dynamical decoupling, + remapping and optimisation as `off`, `on`, or `default` when the request sets + nothing for it, and whether the gates were to run as written. `sampler-v2` + turns decoupling and twirling off; `native-turns` turns debiasing off; + `cloud-tasks` puts a processor's gates in the service's verbatim box; + `prx-circuits` sends only `circuits` and `shots`, so it sets nothing and says + so. A provider of the open protocol states its own (§5.5 of the protocol), + and a wrapper around another service must. + +`wai-quantum verify` checks the seal and every rule a receipt keeps, and names +each one it breaks (the protocol's §8.4); `--json` gives the verdict as data. + +It proves that your key signed this result for this request: what it says +your host received from the provider and device it names, sent and checked +is your host's word, and the counts are the provider's. It does not prove +that the device ran that program, or ran anything. The counts are not reproducible by recomputation; the same job run again gives other counts. The distance from the exact distribution tells you how far they are from what the circuit gives: a distance well above what the shots alone leave is the -device's error, or a program the device did not run as sent. +device's or the simulator's error, or a program not run as sent. + +A receipt seals the provider's id for the job. For `cloud-tasks` that is the +task's identifier, which names your account's number: sharing the receipt +shares it. ## Connecting a real device: what is yours to do @@ -213,8 +243,11 @@ WAI ships no account and no key. To run on a real device: 2. Mint a credential (an API key or a token) in the provider's own console. 3. Add it: `wai-quantum providers add NAME --kind … --credential-stdin`, with the settings its kind takes (`crn` and `backend` for `sampler-v2`, `qc` for - `prx-circuits`, `backend` for `native-turns`), or `--kind http` for a - provider of the open protocol. + `prx-circuits`, `backend` for `native-turns`, `region`, `device` and `bucket` + for `cloud-tasks`), or `--kind http` for a provider of the open protocol. + For `cloud-tasks`, first turn the service on in your account. The bucket must + be in the device's region, its name must begin `amazon-braket-`, and its policy must let the service write to it. A name with a dot works: it is read by path. A region or a bucket that is not a name is refused when the provider is added, and this kind takes no `--endpoint`. + On one of its simulators, `--set basis=gpizz` compiles to that processor basis, so you can check the gates before paying for the processor. 4. Run `wai-quantum providers describe NAME`, then `wai-quantum run probe.qasm --provider NAME --dry-run` to see the compile and the estimate, then the probe itself, confirmed with `--yes` if it is not free. diff --git a/wai/wai-quantum-qpu/schemas/provider.v1.json b/wai/wai-quantum-qpu/schemas/provider.v1.json index 281ce01b4..fc0eba957 100644 --- a/wai/wai-quantum-qpu/schemas/provider.v1.json +++ b/wai/wai-quantum-qpu/schemas/provider.v1.json @@ -180,7 +180,8 @@ ] }, "max_shots": { "type": "integer", "minimum": 1 }, - "seeded": { "type": "boolean" } + "seeded": { "type": "boolean" }, + "approximate": { "type": "boolean", "description": "Optional, with provenance simulator or emulator: true when the simulator's results are approximate (a truncated tensor network, a sampled Pauli path); a result is then labelled approximate (section 5.1)." } } }, "estimate_answer": { @@ -220,7 +221,23 @@ "shots": { "type": "integer", "minimum": 1 }, "calibration": { "type": "string" }, "charged": { "type": "object", "required": ["amount", "currency"], "properties": { "amount": { "type": "string" }, "currency": { "type": "string" } } }, - "energy": { "type": "object", "properties": { "kwh": { "type": "number", "minimum": 0 } } } + "energy": { "type": "object", "properties": { "kwh": { "type": "number", "minimum": 0 } } }, + "counts_source": { "enum": ["shots", "vendor_counts", "probabilities"], "description": "Optional (section 5.5): one record a shot, tallied (shots); the provider's own counts (vendor_counts, how an answer that does not say is read); or counts rebuilt from probabilities, derived and not shot records (probabilities)." }, + "execution": { + "type": "object", + "description": "Optional (section 5.5): how the device ran the program. Each of the five steps is off, on, none (the device has no such step), or unknown (a step the provider cannot vouch for); verbatim says whether the gates ran as written. A provider that relays a job to another service states it.", + "required": ["mitigation", "twirling", "decoupling", "remapping", "optimisation", "verbatim"], + "properties": { + "mitigation": { "enum": ["off", "on", "none", "unknown"] }, + "twirling": { "enum": ["off", "on", "none", "unknown"] }, + "decoupling": { "enum": ["off", "on", "none", "unknown"] }, + "remapping": { "enum": ["off", "on", "none", "unknown"] }, + "optimisation": { "enum": ["off", "on", "none", "unknown"] }, + "verbatim": { "type": "boolean" }, + "note": { "type": "string" } + }, + "additionalProperties": false + } } } } diff --git a/wai/wai-quantum-qpu/src/bin/wai-qpu.rs b/wai/wai-quantum-qpu/src/bin/wai-qpu.rs index 684b8795e..5191a2c7c 100644 --- a/wai/wai-quantum-qpu/src/bin/wai-qpu.rs +++ b/wai/wai-quantum-qpu/src/bin/wai-qpu.rs @@ -3,16 +3,17 @@ //! //! ```text //! wai-qpu device -//! wai-qpu probe [--qubits N] [--shots N] +//! wai-qpu probe [--qubits N] [--shots N] [--yes] //! wai-qpu certify [--shots N] [--cases N] [--report FILE] [--yes] //! wai-qpu reference --stdio | --http ADDR [device options] //! ``` //! -//! `` is `sampler-v2`, `prx-circuits` or `native-turns`, each +//! `` is `sampler-v2`, `prx-circuits`, `native-turns` or `cloud-tasks`, each //! configured from its environment variables (see the crate's README). A -//! certification runs every circuit of the corpus in both gatesets as a job; on -//! a device whose provenance is silicon it spends the service's quota, so it -//! asks for `--yes`. +//! certification runs every circuit of the corpus in both gatesets as a job. On +//! a device whose provenance is silicon, or one the service does not state is +//! free, a job spends the person's quota or money, so a probe and a +//! certification there ask for `--yes`. //! //! `reference` serves the reference provider of the open provider protocol //! (`wai_quantum_qpu::reference`): on standard input and output, or over @@ -21,13 +22,14 @@ use std::process::ExitCode; use wai_quantum::quantum_fabric::Provenance; use wai_quantum::quantum_fabric::sampling::{QPU_DEFAULT, QpuCorpus}; +use wai_quantum_qpu::cloud_tasks::{CloudTasks, Keys}; use wai_quantum_qpu::native_turns::NativeTurns; use wai_quantum_qpu::prx_circuits::PrxCircuits; use wai_quantum_qpu::sampler_v2::SamplerV2; use wai_quantum_qpu::transport::Curl; -use wai_quantum_qpu::{Provider, QpuError, QpuSampler, basis_label, check_probe, probe_circuit, report}; +use wai_quantum_qpu::{Billing, Provider, QpuError, QpuSampler, basis_label, check_probe, probe_circuit, report}; -const USAGE: &str = "usage: wai-qpu device|probe|certify [--qubits N] [--shots N] [--cases N] [--report FILE] [--yes]\n wai-qpu reference --stdio | --http ADDR [device options]"; +const USAGE: &str = "usage: wai-qpu device|probe|certify [--qubits N] [--shots N] [--cases N] [--report FILE] [--yes]\n wai-qpu reference --stdio | --http ADDR [device options]"; #[derive(Debug)] struct Args { @@ -64,17 +66,42 @@ fn provider(service: &str) -> Result, QpuError> { "sampler-v2" => Box::new(SamplerV2::from_env(curl)?), "prx-circuits" => Box::new(PrxCircuits::from_env(curl)?), "native-turns" => Box::new(NativeTurns::from_env(curl)?), - other => return Err(QpuError::Config(format!("no service {other:?}: sampler-v2, prx-circuits or native-turns"))), + "cloud-tasks" => { + let var = |name: &str| std::env::var(name).map_err(|_| QpuError::Config(format!("{name} is not set"))); + let mut c = CloudTasks::new(curl, Keys::from_env()?, var("WAI_QPU_TASKS_REGION")?, var("WAI_QPU_TASKS_DEVICE")?, var("WAI_QPU_TASKS_BUCKET")?)?; + if let Ok(prefix) = var("WAI_QPU_TASKS_PREFIX") { + c = c.with_prefix(prefix); + } + if let Ok(basis) = var("WAI_QPU_TASKS_BASIS") { + c = c.with_basis(wai_quantum_qpu::open_provider::basis_named(&basis).ok_or_else(|| QpuError::Config(format!("no basis {basis:?}: rzsxxcz, prxcz or gpizz")))?); + } + Box::new(c) + } + other => return Err(QpuError::Config(format!("no service {other:?}: sampler-v2, prx-circuits, native-turns or cloud-tasks"))), }) } +/// Whether running on this device spends something of the person's: always on +/// a processor, and on anything the service does not state is free. +fn spends(provenance: &Provenance, billing: &Billing) -> bool { + *provenance == Provenance::Silicon || *billing != Billing::Free +} + fn run(a: Args) -> Result<(), String> { - let sampler = QpuSampler::new(provider(&a.service).map_err(|e| e.to_string())?).map_err(|e| e.to_string())?; + let service = provider(&a.service).map_err(|e| e.to_string())?; + let sampler = QpuSampler::new(&*service).map_err(|e| e.to_string())?; let d = sampler.device(); + let confirmed = |what: String| -> Result<(), String> { + if !a.yes && spends(&d.provenance, &service.details().map_err(|e| e.to_string())?.billing) { + return Err(format!("{what} on {} and spends what the service charges; add --yes to run", d.name)); + } + Ok(()) + }; println!("device: {} · {} qubits · basis {} · {}", d.name, d.coupling.qubits(), basis_label(d.basis), d.provenance.label()); match a.command.as_str() { "device" => Ok(()), "probe" => { + confirmed(format!("probing runs a job of {} shots", a.shots))?; let (counts, job) = sampler.run(&probe_circuit(a.qubits), a.shots).map_err(|e| e.to_string())?; println!("job {}: {} shots, {} on index 1", job.id, job.shots, counts.get(1).copied().unwrap_or(0)); check_probe(&counts, 0.8)?; @@ -84,9 +111,7 @@ fn run(a: Args) -> Result<(), String> { "certify" => { let corpus = QpuCorpus { cases: a.cases.unwrap_or(QPU_DEFAULT.cases), ..QPU_DEFAULT }; let jobs = 2 * corpus.cases; - if d.provenance == Provenance::Silicon && !a.yes { - return Err(format!("certifying runs {jobs} jobs of {} shots on a processor and spends the service's quota; add --yes to run them", a.shots)); - } + confirmed(format!("certifying runs {jobs} jobs of {} shots", a.shots))?; println!("certifying: {jobs} jobs of {} shots, corpus {}", a.shots, corpus.generator()); let member = sampler.member(corpus, a.shots).map_err(|e| e.to_string())?; let r = report(&sampler, &member); @@ -141,4 +166,16 @@ mod tests { assert!(args("probe x --bogus").unwrap_err().contains("unknown flag")); assert!(provider("elsewhere").is_err()); } + + #[test] + fn a_run_spends_unless_the_service_says_it_is_free() { + let simulator = Provenance::Simulator("a simulator".into()); + let priced = Billing::Priced { unit: "minute".into(), price: "0.075".into(), currency: "USD".into() }; + let unknown = Billing::Unknown("no price stated".into()); + assert!(spends(&Provenance::Silicon, &Billing::Free)); + assert!(spends(&Provenance::Silicon, &unknown)); + assert!(spends(&simulator, &priced), "a simulator with a price spends"); + assert!(!spends(&simulator, &Billing::Free)); + assert!(spends(&simulator, &unknown), "a price no one states is no promise that it is free"); + } } diff --git a/wai/wai-quantum-qpu/src/cloud_tasks.rs b/wai/wai-quantum-qpu/src/cloud_tasks.rs new file mode 100644 index 000000000..84abf247f --- /dev/null +++ b/wai/wai-quantum-qpu/src/cloud_tasks.rs @@ -0,0 +1,1129 @@ +//! A cloud service of quantum tasks, reached with the person's own account. +//! +//! - **Auth**: every request is signed with the person's own keys (Signature +//! Version 4), so nothing secret crosses the wire and nothing is held but +//! the keys, where the person keeps them. The keys never reach a log, a +//! receipt or the hosted service. +//! - **Device**: `GET /device/{arn}`: its status, its type and its capabilities +//! (native gates, qubits, connectivity, shots and price). A processor that +//! runs one of the crate's bases as its native gates is driven in them; a +//! simulator takes `rz sx x cz`. +//! - **Submit**: `POST /quantum-task`: the program as OpenQASM 3, every angle an +//! exact multiple of pi. On a processor it is a verbatim box on physical +//! qubits, so the device runs the program as compiled and checked here. The +//! result goes to a storage bucket of the person's own. +//! - **Results**: `GET /quantum-task/{arn}` names where they are, and the +//! bucket's `results.json` holds them: one row of bits per shot, or a +//! probability for each outcome, in the order of its `measuredQubits`, read +//! back onto logical qubits through the device's names for the qubits the +//! program ended on, as they were when it was sent. A bucket whose name has +//! a dot is read by path: the storage service's certificate covers one label +//! before its domain. +//! +//! A task is priced by the device (per shot, or per minute on a simulator), and +//! on a processor the service also charges per task. Its API states only the +//! device's price, so [`Provider::estimate`] adds the fee from the service's +//! price list and says where each part comes from. + +use crate::transport::{Request, Transport}; +use crate::{Billing, Cost, CountsSource, Details, Device, Estimate, JobInfo, JobStatus, Provider, QpuError, QubitNames, Secret, Stated, counts_from_probabilities, field, field_str, json_ok}; +use wai_quantum::engine::provider::client_execution; +use serde_json::{Value, json}; +use sha2::{Digest, Sha256}; +use std::cell::RefCell; +use std::time::{SystemTime, UNIX_EPOCH}; +use wai_quantum::quantum_fabric::Provenance; +use wai_quantum::quantum_fabric::sampling::Counts; +use wai_quantum::quantum_toolchain::device::counts_from_integers; +use wai_quantum::quantum_toolchain::native::{Basis, Coupling, Dyadic, NativeGate, NativeProgram}; + +/// The protocol's name, as a kind and in a device's name. +pub const PROTOCOL: &str = "cloud-tasks"; +/// The signing name of the task service. +const SERVICE: &str = "braket"; // allow-name: the service's signing name +/// The service's fee for one task on a processor, in USD, the same on every +/// processor. Its API does not state it; this is its price list of 2026-11-05. +const TASK_FEE: f64 = 0.30; +/// The signing name of the storage service. +const STORAGE: &str = "s3"; +/// The line that opens the service's verbatim box: the gates inside run as +/// written, on the physical qubits named. +const VERBATIM: &str = "#pragma braket verbatim"; // allow-name: the service's pragma + +/// The person's keys: an id, a secret, and a session token when they are +/// temporary. +#[derive(Clone)] +pub struct Keys { + id: String, + secret: Secret, + token: Option, +} + +impl Keys { + pub fn new(id: impl Into, secret: Secret, token: Option) -> Self { + Keys { id: id.into(), secret, token } + } + + /// From one kept credential, `id:secret` or `id:secret:token`. + pub fn parse(credential: &Secret) -> Result { + let mut parts = credential.expose().splitn(3, ':'); + match (parts.next(), parts.next(), parts.next()) { + (Some(id), Some(secret), token) if !id.is_empty() && !secret.is_empty() => { + Ok(Keys::new(id, Secret::new(secret), token.filter(|t| !t.is_empty()).map(Secret::new))) + } + _ => Err(QpuError::Config("cloud-tasks takes its credential as id:secret or id:secret:token".into())), + } + } + + /// From the environment variables the service's own tools read. + pub fn from_env() -> Result { + let id = std::env::var("AWS_ACCESS_KEY_ID").ok().filter(|v| !v.is_empty()); // allow-name: the account's own variables + let id = id.ok_or_else(|| QpuError::Config("cloud-tasks needs its keys: --credential-stdin, or AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY".into()))?; // allow-name + let token = Secret::from_env("AWS_SESSION_TOKEN").ok(); // allow-name + Ok(Keys::new(id, Secret::from_env("AWS_SECRET_ACCESS_KEY")?, token)) // allow-name + } + + /// Each part as a [`Secret`], to take out of messages: a service quotes a + /// part, and never the kept credential whole. + pub fn parts(&self) -> Vec { + std::iter::once(Secret::new(self.id.clone())).chain(std::iter::once(self.secret.clone())).chain(self.token.clone()).collect() + } +} + +/// Whether `region` is a region's name: it goes into the host requests are +/// signed for and sent to. +pub fn check_region(region: &str) -> Result<(), String> { + let ok = !region.is_empty() && region.len() <= 32 && region.split('-').count() >= 2 && region.split('-').all(|part| !part.is_empty() && part.bytes().all(|b| b.is_ascii_lowercase() || b.is_ascii_digit())); + if ok { Ok(()) } else { Err(format!("{region:?} is not a region: it reads like us-east-1, in lower-case letters, digits and hyphens")) } +} + +/// Whether `bucket` is a storage bucket's name, by the service's rules: it +/// goes into the host its results are read from. +pub fn check_bucket(bucket: &str) -> Result<(), String> { + let edge = |b: Option<&u8>| b.is_some_and(|b| b.is_ascii_lowercase() || b.is_ascii_digit()); + let address = bucket.split('.').count() == 4 && bucket.split('.').all(|part| part.parse::().is_ok()); + let ok = (3..=63).contains(&bucket.len()) + && bucket.bytes().all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'.' || b == b'-') + && edge(bucket.as_bytes().first()) + && edge(bucket.as_bytes().last()) + && !bucket.contains("..") + && !bucket.contains(".-") + && !bucket.contains("-.") + && !address; + if ok { Ok(()) } else { Err(format!("{bucket:?} is not a bucket's name: 3 to 63 of lower-case letters, digits, dots and hyphens, starting and ending with a letter or a digit, with no two dots together, and not an address")) } +} + +/// What the service says of a device. +#[derive(Clone, Debug)] +struct Caps { + name: String, + provider: String, + status: String, + simulator: bool, + basis: Basis, + /// The device takes `sx` and `x` as `rx`. + rx: bool, + /// The device's own name for each index of the coupling map. + labels: Vec, + coupling: Coupling, + price: Option<(String, String)>, + updated: Option, + max_shots: Option, +} + +/// A client for one device, on the person's own account. +pub struct CloudTasks { + transport: T, + keys: Keys, + region: String, + device: String, + bucket: String, + prefix: String, + /// A test server, in place of the task service's own host for a region. + base: Option, + basis: Option, + caps: RefCell>, + /// The tasks this client submitted: their qubits are the ones it read. + submitted: RefCell>, +} + +/// The region a task's id names (`arn:…:REGION:ACCOUNT:quantum-task/ID`), when +/// it names one: a task is where it was created, whatever its entry says by now. +fn region_of(id: &str) -> Option<&str> { + let parts: Vec<&str> = id.splitn(6, ':').collect(); + (parts.len() == 6 && parts[0] == "arn" && parts[2] == SERVICE && parts[5].starts_with("quantum-task/") && check_region(parts[3]).is_ok()).then(|| parts[3]) +} + +impl CloudTasks { + /// A client, refused when `region` or `bucket` could not be a name: they + /// go into the hosts that are sent signed requests. + pub fn new(transport: T, keys: Keys, region: impl Into, device: impl Into, bucket: impl Into) -> Result { + let (region, bucket) = (region.into(), bucket.into()); + check_region(®ion).and_then(|()| check_bucket(&bucket)).map_err(QpuError::Config)?; + Ok(CloudTasks { + transport, + keys, + base: None, + region, + device: device.into(), + bucket, + prefix: "wai-quantum".into(), + basis: None, + caps: RefCell::new(None), + submitted: RefCell::new(std::collections::HashSet::new()), + }) + } + + /// Where in the bucket results go. + pub fn with_prefix(mut self, prefix: impl Into) -> Self { + self.prefix = prefix.into(); + self + } + + /// On a simulator, compile to this basis, to check a processor's gates + /// before paying for a processor. A processor runs its own native gates and + /// refuses this. + pub fn with_basis(mut self, basis: Basis) -> Self { + self.basis = Some(basis); + self + } + + /// A test server in place of the task service. Results are read from the + /// storage service's own hosts, as the bucket's name addresses them. + pub fn with_base(mut self, base: impl Into) -> Self { + self.base = Some(base.into()); + self + } + + /// The task service in `region`. + fn base(&self, region: &str) -> String { + self.base.clone().unwrap_or_else(|| format!("https://{SERVICE}.{region}.amazonaws.com")) // allow-name: the service's endpoint + } + + /// The region of task `id`: the one its id names, else this client's. + fn region_of_task<'a>(&'a self, id: &'a str) -> &'a str { + region_of(id).unwrap_or(&self.region) + } + + /// Where a task's results are, in `bucket` of `region`. A name with a dot is + /// read by path: the service's wildcard certificate covers one label. + fn results_url(region: &str, bucket: &str, directory: &str) -> String { + let key = encode_path(directory); + if bucket.contains('.') { + format!("https://{STORAGE}.{region}.amazonaws.com/{bucket}/{key}/results.json") // allow-name: the service's endpoint + } else { + format!("https://{bucket}.{STORAGE}.{region}.amazonaws.com/{key}/results.json") // allow-name: the service's endpoint + } + } + + fn signed(&self, request: Request, service: &str, region: &str) -> Request { + sign(request, service, region, &self.keys, now_stamp(SystemTime::now())) + } + + fn capabilities(&self) -> Result { + if let Some(c) = self.caps.borrow().as_ref() { + return Ok(c.clone()); + } + let url = format!("{}/device/{}", self.base(&self.region), encode(&self.device)); + let v = json_ok(&self.transport.send(&self.signed(Request::get(url).header("Accept", "application/json"), SERVICE, &self.region))?)?; + let mut caps = read_device(&v)?; + if let Some(basis) = self.basis { + if !caps.simulator { + return Err(QpuError::Config(format!("{} is a processor and runs its own native gates: the setting basis is for a simulator", caps.name))); + } + caps.basis = basis; + } + *self.caps.borrow_mut() = Some(caps.clone()); + Ok(caps) + } + + /// The program as this device takes it. + fn source(&self, program: &NativeProgram) -> Result { + let caps = self.capabilities()?; + if program.basis != caps.basis { + return Err(QpuError::Config(format!("{} does not run this program's gates", caps.name))); + } + openqasm(program, &caps) + } + + /// The task's body, as [`Provider::submit`] sends it, and whether its + /// program's gates are in the verbatim box. + fn body(&self, program: &NativeProgram, shots: u64) -> Result<(Value, bool), QpuError> { + let source = self.source(program)?; + let verbatim = source.lines().any(|l| l == VERBATIM); + let action = json!({ + "braketSchemaHeader": {"name": "braket.ir.openqasm.program", "version": "1"}, // allow-name: the service's schema + "source": source, + }); + let body = json!({ + "action": action.to_string(), + "clientToken": client_token(source.as_bytes()), + "deviceArn": self.device, + "outputS3Bucket": self.bucket, + "outputS3KeyPrefix": self.prefix, + "shots": shots, + }); + Ok((body, verbatim)) + } +} + +/// A byte string's SHA-256, in hexadecimal. +fn hex_sha256(bytes: &[u8]) -> String { + Sha256::digest(bytes).iter().map(|b| format!("{b:02x}")).collect() +} + +/// HMAC-SHA-256 (RFC 2104). +fn hmac(key: &[u8], message: &[u8]) -> [u8; 32] { + let mut block = [0u8; 64]; + if key.len() > 64 { + block[..32].copy_from_slice(&Sha256::digest(key)); + } else { + block[..key.len()].copy_from_slice(key); + } + let (mut inner, mut outer) = (Sha256::new(), Sha256::new()); + inner.update(block.map(|b| b ^ 0x36)); + inner.update(message); + outer.update(block.map(|b| b ^ 0x5c)); + outer.update(inner.finalize()); + outer.finalize().into() +} + +/// A path segment's percent-encoding: everything but the unreserved characters. +fn encode(text: &str) -> String { + let mut out = String::with_capacity(text.len()); + for b in text.bytes() { + if b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_' | b'.' | b'~') { + out.push(b as char); + } else { + out.push_str(&format!("%{b:02X}")); + } + } + out +} + +/// A key or path whose `/` stay separators, each segment encoded. +fn encode_path(path: &str) -> String { + path.split('/').map(encode).collect::>().join("/") +} + +/// The instant a request is signed at: `YYYYMMDDTHHMMSSZ`. +fn now_stamp(now: SystemTime) -> String { + let secs = now.duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0); + let (days, rest) = ((secs / 86400) as i64, secs % 86400); + // Civil from days (Howard Hinnant's algorithm). + let z = days + 719468; + let era = z.div_euclid(146097); + let doe = z.rem_euclid(146097); + let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; + let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); + let mp = (5 * doy + 2) / 153; + let d = doy - (153 * mp + 2) / 5 + 1; + let m = if mp < 10 { mp + 3 } else { mp - 9 }; + let y = yoe + era * 400 + i64::from(m <= 2); + format!("{y:04}{m:02}{d:02}T{:02}{:02}{:02}Z", rest / 3600, rest % 3600 / 60, rest % 60) +} + +/// The signature over a canonical request: `headers` are lower-case and sorted. +#[allow(clippy::too_many_arguments)] +fn signature(method: &str, canonical_uri: &str, query: &str, headers: &[(String, String)], payload_hash: &str, secret: &str, stamp: &str, region: &str, service: &str) -> String { + let lines: String = headers.iter().map(|(n, v)| format!("{n}:{}\n", v.trim())).collect(); + let names: Vec<&str> = headers.iter().map(|(n, _)| n.as_str()).collect(); + let canonical = format!("{method}\n{canonical_uri}\n{query}\n{lines}\n{}\n{payload_hash}", names.join(";")); + let day = &stamp[..8]; + let scope = format!("{day}/{region}/{service}/aws4_request"); // allow-name: the signature's literal + let to_sign = format!("AWS4-HMAC-SHA256\n{stamp}\n{scope}\n{}", hex_sha256(canonical.as_bytes())); // allow-name + let key = [day, region, service, "aws4_request"] // allow-name + .iter() + .fold(format!("AWS4{secret}").into_bytes(), |k, part| hmac(&k, part.as_bytes()).to_vec()); // allow-name + hmac(&key, to_sign.as_bytes()).iter().map(|b| format!("{b:02x}")).collect() +} + +/// `request` with the headers that sign it. Its URL's path is as it will be sent. +/// The task service's canonical path encodes that path once more; storage's is +/// the path as sent. +fn sign(request: Request, service: &str, region: &str, keys: &Keys, stamp: String) -> Request { + let as_sent = service == STORAGE; + sign_with(request, service, region, keys, stamp, true, as_sent) +} + +/// [`sign`], with the choices the published test suite varies: whether the +/// body's hash is a signed header (`x-amz-content-sha256`), and whether the +/// canonical path is the path as sent or that path encoded once more. +fn sign_with(request: Request, service: &str, region: &str, keys: &Keys, stamp: String, sign_body: bool, path_as_sent: bool) -> Request { + let after_scheme = request.url.split_once("://").map_or(request.url.as_str(), |(_, rest)| rest); + let (host, path) = after_scheme.split_once('/').map_or((after_scheme, "/".to_string()), |(h, p)| (h, format!("/{p}"))); + let payload = hex_sha256(request.body.as_deref().unwrap_or_default()); + let uri = if path_as_sent { path.clone() } else { path.split('/').map(encode).collect::>().join("/") }; + let mut headers: Vec<(String, String)> = vec![("host".into(), host.to_string()), ("x-amz-date".into(), stamp.clone())]; + if sign_body { + headers.push(("x-amz-content-sha256".into(), payload.clone())); + } + if let Some(t) = &keys.token { + headers.push(("x-amz-security-token".into(), t.expose().to_string())); + } + for name in ["Content-Type", "Content-Length"] { + if let Some(c) = request.header_value(name) { + headers.push((name.to_ascii_lowercase(), c.to_string())); + } + } + headers.sort(); + let sig = signature(request.method.as_str(), &uri, "", &headers, &payload, keys.secret.expose(), &stamp, region, service); + let names: Vec<&str> = headers.iter().map(|(n, _)| n.as_str()).collect(); + let credential = format!("{}/{}/{region}/{service}/aws4_request", keys.id, &stamp[..8]); // allow-name + let authorization = format!("AWS4-HMAC-SHA256 Credential={credential}, SignedHeaders={}, Signature={sig}", names.join(";")); // allow-name + let mut out = request.header("Authorization", authorization).header("x-amz-date", stamp); + if sign_body { + out = out.header("x-amz-content-sha256", payload); + } + if let Some(t) = &keys.token { + out = out.header("x-amz-security-token", t.expose()); + } + out +} + +/// A dyadic number of turns as an OpenQASM angle, exactly: `m*pi/2^(k-1)`. +fn angle(d: Dyadic) -> String { + let (m, k) = d.turns(); + match k { + 0 => "0".to_string(), + _ => format!("{m}*pi/{}", 1u64 << (k - 1)), + } +} + +/// The program as OpenQASM 3: on a processor, a verbatim box on physical qubits; +/// on a simulator, a register as wide as the program uses. +fn openqasm(program: &NativeProgram, caps: &Caps) -> Result { + let width = program.gates.iter().fold(program.final_layout.iter().copied().max().map_or(0, |q| q + 1), |w, g| match *g { + NativeGate::Rz(q, _) | NativeGate::Sx(q) | NativeGate::X(q) | NativeGate::Prx(q, _, _) | NativeGate::Gpi(q, _) | NativeGate::Gpi2(q, _) => w.max(q + 1), + NativeGate::Cz(a, b) | NativeGate::Zz(a, b, _) => w.max(a.max(b) + 1), + }); + let name = |q: u16| -> Result { + if caps.simulator { + return Ok(format!("q[{q}]")); + } + caps.labels.get(usize::from(q)).map(|l| format!("${l}")).ok_or_else(|| QpuError::Config(format!("qubit {q} is not on {}", caps.name))) + }; + let mut lines = Vec::with_capacity(program.gates.len()); + for g in &program.gates { + lines.push(match *g { + NativeGate::Rz(q, t) => format!("rz({}) {};", angle(t), name(q)?), + NativeGate::Sx(q) if caps.rx => format!("rx(pi/2) {};", name(q)?), + NativeGate::X(q) if caps.rx => format!("rx(pi) {};", name(q)?), + NativeGate::Sx(q) => format!("v {};", name(q)?), + NativeGate::X(q) => format!("x {};", name(q)?), + NativeGate::Cz(a, b) => format!("cz {}, {};", name(a)?, name(b)?), + NativeGate::Prx(q, t, p) => format!("prx({}, {}) {};", angle(t), angle(p), name(q)?), + NativeGate::Gpi(q, p) => format!("gpi({}) {};", angle(p), name(q)?), + NativeGate::Gpi2(q, p) => format!("gpi2({}) {};", angle(p), name(q)?), + NativeGate::Zz(a, b, t) => format!("zz({}) {}, {};", angle(t), name(a)?, name(b)?), + }); + } + Ok(if caps.simulator { + // The service refuses a register with a qubit no gate touches, and an identity changes nothing. + let mut used = vec![false; usize::from(width)]; + for g in &program.gates { + match *g { + NativeGate::Rz(q, _) | NativeGate::Sx(q) | NativeGate::X(q) | NativeGate::Prx(q, _, _) | NativeGate::Gpi(q, _) | NativeGate::Gpi2(q, _) => used[usize::from(q)] = true, + NativeGate::Cz(a, b) | NativeGate::Zz(a, b, _) => { + used[usize::from(a)] = true; + used[usize::from(b)] = true; + } + } + } + lines.extend(used.iter().enumerate().filter(|(_, u)| !**u).map(|(q, _)| format!("i q[{q}];"))); + // It must measure explicitly, and every qubit of the register. + let measure: Vec = (0..width).map(|q| format!("b[{q}] = measure q[{q}];")).collect(); + format!("OPENQASM 3.0;\nbit[{width}] b;\nqubit[{width}] q;\n{}\n{}\n", lines.join("\n"), measure.join("\n")) + } else { + // Each logical qubit where it ends, in the assigned form every device takes. + let mut measure = Vec::with_capacity(program.final_layout.len()); + for (i, &q) in program.final_layout.iter().enumerate() { + measure.push(format!("b[{i}] = measure {};", name(q)?)); + } + let n = measure.len(); + format!("OPENQASM 3.0;\nbit[{n}] b;\n{VERBATIM}\nbox {{\n{}\n}}\n{}\n", lines.join("\n"), measure.join("\n")) + }) +} + +/// The service's description of a device. +fn read_device(v: &Value) -> Result { + let caps: Value = serde_json::from_str(field_str(v, &["deviceCapabilities"])?).map_err(|e| QpuError::Protocol(format!("deviceCapabilities: {e}")))?; + let simulator = v.get("deviceType").and_then(Value::as_str) == Some("SIMULATOR"); + let name = field_str(v, &["deviceName"])?.to_string(); + let paradigm = field(&caps, &["paradigm"])?; + let n = field(paradigm, &["qubitCount"])?.as_u64().filter(|&n| n > 0 && n <= 1024).ok_or_else(|| QpuError::Protocol("qubitCount".into()))? as u16; + let lower = |list: Option<&Vec>| -> Vec { list.into_iter().flatten().filter_map(Value::as_str).map(str::to_lowercase).collect() }; + let native = lower(paradigm.get("nativeGateSet").and_then(Value::as_array)); + let has = |g: &[&str]| g.iter().all(|x| native.iter().any(|n| n == x)); + // `sx` and `x` are `rx(pi/2)` and `rx(pi)` up to a global phase, which no + // count can see, so a device of `rx rz cz` runs this basis exactly. + let rx = !simulator && has(&["rz", "rx", "cz"]) && !has(&["prx"]) && !has(&["x"]); + let basis = if simulator { + Basis::RzSxXCz + } else if has(&["prx", "cz"]) { + Basis::PrxCz + } else if has(&["gpi", "gpi2", "zz"]) { + Basis::GpiZz + } else if has(&["rz", "v", "x", "cz"]) || has(&["rz", "sx", "x", "cz"]) || rx { + Basis::RzSxXCz + } else { + return Err(QpuError::Unsupported(format!("{name} runs {} as its native gates, and none of the bases this client compiles to", native.join(", ")))); + }; + let connectivity = paradigm.get("connectivity"); + let full = simulator || connectivity.and_then(|c| c.get("fullyConnected")).and_then(Value::as_bool) == Some(true); + let graph = connectivity.and_then(|c| c.get("connectivityGraph")).and_then(Value::as_object); + let (labels, pairs): (Vec, Vec<(u16, u16)>) = if full { + let mut pairs = Vec::new(); + for a in 0..n { + for b in a + 1..n { + pairs.push((a, b)); + } + } + ((0..u64::from(n)).collect(), pairs) + } else { + let graph = graph.ok_or_else(|| QpuError::Protocol("no connectivityGraph".into()))?; + let number = |s: &str| s.parse::().map_err(|_| QpuError::Protocol(format!("qubit label {s:?}"))); + let mut labels = Vec::new(); + for (from, to) in graph { + labels.push(number(from)?); + for t in to.as_array().into_iter().flatten().filter_map(Value::as_str) { + labels.push(number(t)?); + } + } + labels.sort_unstable(); + labels.dedup(); + let index = |l: u64| labels.binary_search(&l).map(|i| i as u16).expect("every label was collected"); + let mut pairs = Vec::new(); + for (from, to) in graph { + for t in to.as_array().into_iter().flatten().filter_map(Value::as_str) { + pairs.push((index(number(from)?), index(number(t)?))); + } + } + (labels, pairs) + }; + let coupling = Coupling::new(labels.len() as u16, &pairs).map_err(|e| QpuError::Protocol(format!("connectivity: {e:?}")))?; + let service = caps.get("service"); + let cost = service.and_then(|s| s.get("deviceCost")); + let price = cost.and_then(|c| { + let p = c.get("price")?; + let p = p.as_str().map(String::from).or_else(|| p.as_f64().map(|f| f.to_string()))?; + Some((p, c.get("unit")?.as_str()?.to_string())) + }); + Ok(Caps { + provider: v.get("providerName").and_then(Value::as_str).unwrap_or("").to_string(), + status: v.get("deviceStatus").and_then(Value::as_str).unwrap_or("").to_string(), + simulator, + basis, + rx, + labels, + coupling, + price, + updated: service.and_then(|s| s.get("updatedAt")).and_then(Value::as_str).map(String::from), + max_shots: service.and_then(|s| s.get("shotsRange")).and_then(Value::as_array).and_then(|r| r.get(1)).and_then(Value::as_u64), + name, + }) +} + +/// A client token: the service takes a task once under it, and it is new for +/// every submission. A submission that may have arrived is never sent again. +fn client_token(seed: &[u8]) -> String { + let mut h = blake3::Hasher::new(); + h.update(seed); + h.update(&std::process::id().to_le_bytes()); + h.update(&SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_nanos()).unwrap_or(0).to_le_bytes()); + let x = h.finalize().to_hex().to_string(); + format!("{}-{}-{}-{}-{}", &x[..8], &x[8..12], &x[12..16], &x[16..20], &x[20..32]) +} + +/// The device's name for every qubit `program` uses: on a simulator, its index +/// in the register. +fn names_of(program: &NativeProgram, caps: &Caps) -> Result { + crate::program_qubits(program) + .into_iter() + .map(|q| { + let label = if caps.simulator { Some(u64::from(q)) } else { caps.labels.get(usize::from(q)).copied() }; + Ok((q, label.ok_or_else(|| QpuError::Config(format!("qubit {q} is not on {}", caps.name)))?.to_string())) + }) + .collect() +} + +/// Counts from a task's `results.json`, read back onto logical qubits (logical +/// qubit `i` is the measured qubit whose label is `ends[i]`), and where they +/// come from: its rows of bits, one per shot, or, when it has none, counts +/// rebuilt from its probabilities. +fn read_counts(v: &Value, ends: &[u64], n_qubits: u8, shots: u64) -> Result<(Counts, CountsSource), QpuError> { + let measured: Vec = field(v, &["measuredQubits"])?.as_array().and_then(|m| m.iter().map(Value::as_u64).collect()).ok_or_else(|| QpuError::Protocol("measuredQubits".into()))?; + if ends.len() != usize::from(n_qubits) { + return Err(QpuError::Counts(format!("{} qubits ended on, of a program of {n_qubits}", ends.len()))); + } + let places: Vec = ends.iter().map(|label| measured.iter().position(|q| q == label).ok_or_else(|| QpuError::Counts(format!("qubit {label}, which the program ended on, is not among those measured")))).collect::>()?; + let logical = |one: &dyn Fn(usize) -> bool| -> usize { places.iter().enumerate().map(|(i, &at)| usize::from(one(at)) << i).sum() }; + if let Some(rows) = v.get("measurements").and_then(Value::as_array).filter(|r| !r.is_empty()) { + let mut values = Vec::with_capacity(rows.len()); + for row in rows { + let row = row.as_array().filter(|r| r.len() == measured.len()).ok_or_else(|| QpuError::Counts("a shot's row is not one bit per measured qubit".into()))?; + let bits: Vec = row.iter().map(|b| b.as_u64().filter(|&b| b <= 1)).collect::>().ok_or_else(|| QpuError::Counts("a shot's row holds something other than bits".into()))?; + values.push((logical(&|at| bits[at] == 1) as u64, 1u64)); + } + return counts_from_integers(n_qubits, values).map(|c| (c, CountsSource::Shots)).map_err(|e| QpuError::Counts(e.to_string())); + } + let map = v.get("measurementProbabilities").and_then(Value::as_object).ok_or_else(|| QpuError::Counts("neither measurements nor measurementProbabilities".into()))?; + let mut p = vec![0.0f64; 1usize << n_qubits]; + for (outcome, prob) in map { + let prob = prob.as_f64().filter(|x| x.is_finite() && *x >= 0.0).ok_or_else(|| QpuError::Counts(format!("probability of {outcome:?}")))?; + if outcome.len() != measured.len() { + return Err(QpuError::Counts(format!("outcome {outcome:?} is not one bit per measured qubit"))); + } + let bits = outcome.as_bytes(); + p[logical(&|at| bits[at] == b'1')] += prob; + } + Ok((counts_from_probabilities(&p, shots), CountsSource::Probabilities)) +} + +impl Provider for CloudTasks { + fn device(&self) -> Result { + let c = self.capabilities()?; + let provenance = if c.simulator { Provenance::Simulator(format!("the service's {} simulator", c.name)) } else { Provenance::Silicon }; + Ok(Device { name: format!("{PROTOCOL}:{}", c.name), basis: c.basis, coupling: c.coupling, provenance }) + } + + fn details(&self) -> Result { + let device = self.device()?; + let c = self.capabilities()?; + let mut d = Details::of(PROTOCOL, &device); + d.calibration = c.updated.clone(); + d.billing = match &c.price { + Some((price, unit)) => Billing::Priced { unit: unit.to_lowercase(), price: price.clone(), currency: "USD".into() }, + None => Billing::Unknown("the service states no price for this device".into()), + }; + d.describe["provider"] = json!(c.provider); + d.describe["status"] = json!(c.status); + d.describe["labels"] = json!(c.labels); + if let Some(m) = c.max_shots { + d.describe["max_shots"] = json!(m); + } + Ok(d) + } + + fn estimate(&self, _program: &NativeProgram, shots: u64) -> Result, QpuError> { + let c = self.capabilities()?; + let Some((price, unit)) = &c.price else { return Ok(None) }; + let each: f64 = price.parse().map_err(|_| QpuError::Protocol(format!("a price of {price:?}")))?; + let (amount, at_least, note) = if unit.eq_ignore_ascii_case("shot") { + (TASK_FEE + each * shots as f64, false, "the device's price per shot, which its API states, and the service's fee per task of 0.30 USD, from its price list of 2026-11-05, which its API does not state") + } else if unit.eq_ignore_ascii_case("minute") { + (each / 20.0, true, "the device's price for its three-second minimum, at least; the time a task takes is not known before it runs") + } else { + return Ok(None); + }; + Ok(Some(Estimate { + free: false, + cost: Some(Cost { amount: format!("{amount:.6}"), currency: "USD".into() }), + unit: Some(unit.to_lowercase()), + seconds: None, + note: Some(note.into()), + at_least, + })) + } + + fn submit(&self, program: &NativeProgram, shots: u64) -> Result { + let (body, _) = self.body(program, shots)?; + let request = Request::post(format!("{}/quantum-task", self.base(&self.region)), body.to_string().into_bytes()).header("Content-Type", "application/json"); + let r = self.transport.send(&self.signed(request, SERVICE, &self.region))?; + let id = field_str(&json_ok(&r)?, &["quantumTaskArn"])?.to_string(); + self.submitted.borrow_mut().insert(id.clone()); + Ok(id) + } + + fn status(&self, id: &str) -> Result { + let region = self.region_of_task(id); + let url = format!("{}/quantum-task/{}", self.base(region), encode(id)); + let v = json_ok(&self.transport.send(&self.signed(Request::get(url).header("Accept", "application/json"), SERVICE, region))?)?; + Ok(match field_str(&v, &["status"])? { + "CREATED" | "QUEUED" | "RUNNING" | "CANCELLING" => JobStatus::Pending, + "COMPLETED" => JobStatus::Done, + other => JobStatus::Ended { status: other.to_lowercase(), reason: v.get("failureReason").and_then(Value::as_str).unwrap_or("").to_string() }, + }) + } + + fn execution(&self, program: &NativeProgram) -> Option { + let (body, verbatim) = self.body(program, 1).ok()?; + Some(if verbatim { + client_execution(&body, &[], &[("verbatim_box", json!(true))], ["default", "default", "default", "off", "off"], true, "the processor's gates go in the service's verbatim box on its physical qubits, so the service neither remaps nor optimises them; the task sets no device parameters, so mitigation, twirling and decoupling are the service's defaults") + } else { + client_execution(&body, &[], &[], ["default"; 5], false, "a simulator's task: no verbatim box and no device parameters, so every step is the service's default") + }) + } + + fn counts(&self, id: &str, program: &NativeProgram, shots: u64) -> Result<(Counts, JobInfo), QpuError> { + self.counts_stated(id, program, shots, None).map(|(c, i, _)| (c, i)) + } + + fn qubit_names(&self, program: &NativeProgram) -> Result, QpuError> { + names_of(program, &self.capabilities()?).map(Some) + } + + fn counts_stated(&self, id: &str, program: &NativeProgram, shots: u64, names: Option<&QubitNames>) -> Result<(Counts, JobInfo, Stated), QpuError> { + let unrecorded; + let names = match names { + Some(names) => names, + None => { + // No record: only the client that submitted a task on a processor + // knows what its indices meant then, and another does not guess. + let sent = self.submitted.borrow().contains(id); + let caps = self.capabilities()?; + if !sent && !caps.simulator { + return Err(QpuError::Counts(format!("nothing here says which qubits of {} this job was sent on: it is read by the client that sent it, or through its record", caps.name))); + } + unrecorded = names_of(program, &caps)?; + &unrecorded + } + }; + let ends: Vec = program.final_layout.iter().map(|q| names.get(q).and_then(|n| n.parse().ok()).ok_or_else(|| QpuError::Counts(format!("the job's record does not name qubit {q}")))).collect::>()?; + let region = self.region_of_task(id); + let url = format!("{}/quantum-task/{}", self.base(region), encode(id)); + let task = json_ok(&self.transport.send(&self.signed(Request::get(url).header("Accept", "application/json"), SERVICE, region))?)?; + let directory = field_str(&task, &["outputS3Directory"])?.trim_end_matches('/'); + // The bucket the task itself wrote to, in its region, whatever the entry names by now. + let bucket = task.get("outputS3Bucket").and_then(Value::as_str).unwrap_or(&self.bucket); + check_bucket(bucket).map_err(QpuError::Protocol)?; + let results = json_ok(&self.transport.send(&self.signed(Request::get(Self::results_url(region, bucket, directory)), STORAGE, region))?)?; + let (counts, source) = read_counts(&results, &ends, program.n_qubits, shots)?; + Ok((counts, JobInfo::default(), Stated::counts(source))) + } + + fn cancel(&self, id: &str) -> Result<(), QpuError> { + let region = self.region_of_task(id); + let url = format!("{}/quantum-task/{}/cancel", self.base(region), encode(id)); + let body = json!({ "clientToken": client_token(id.as_bytes()) }).to_string().into_bytes(); + let request = Request::put(url, body).header("Content-Type", "application/json"); + json_ok(&self.transport.send(&self.signed(request, SERVICE, region))?).map(|_| ()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::transport::{Response, Scripted}; + + fn keys() -> Keys { + Keys::new("AKIDEXAMPLE", Secret::new("wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"), None) + } + + #[test] + fn the_signature_is_the_published_example() { + // The worked example the signing scheme's documentation gives. + let headers = vec![ + ("content-type".to_string(), "application/x-www-form-urlencoded; charset=utf-8".to_string()), + ("host".to_string(), "iam.amazonaws.com".to_string()), // allow-name: the published example + ("x-amz-date".to_string(), "20150830T123600Z".to_string()), + ]; + let payload = hex_sha256(b""); + assert_eq!(payload, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + let sig = signature("GET", "/", "Action=ListUsers&Version=2010-05-08", &headers, &payload, "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", "20150830T123600Z", "us-east-1", "iam"); + assert_eq!(sig, "5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7"); + } + + #[test] + fn hmac_is_rfc_4231() { + // RFC 4231, test case 2. + let out: String = hmac(b"Jefe", b"what do ya want for nothing?").iter().map(|b| format!("{b:02x}")).collect(); + assert_eq!(out, "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"); + } + + #[test] + fn a_stamp_is_the_civil_date_in_utc() { + let at = |s: u64| now_stamp(UNIX_EPOCH + std::time::Duration::from_secs(s)); + assert_eq!(at(0), "19700101T000000Z"); + assert_eq!(at(1440938160), "20150830T123600Z"); + assert_eq!(at(1709210096), "20240229T123456Z"); + } + + #[test] + fn the_task_service_signs_its_path_encoded_again_and_storage_does_not() { + let arn = "arn:aws:braket:::device/quantum-simulator/amazon/sv1"; // allow-name: a device's own name + let url = format!("https://svc.example/device/{}", encode(arn)); + assert!(url.ends_with("arn%3Aaws%3Abraket%3A%3A%3Adevice%2Fquantum-simulator%2Famazon%2Fsv1")); // allow-name + let once = sign(Request::get(url.clone()), SERVICE, "us-east-1", &keys(), "20261105T000000Z".into()); + let twice = sign(Request::get(url), STORAGE, "us-east-1", &keys(), "20261105T000000Z".into()); + let sig = |r: &Request| r.header_value("Authorization").unwrap().rsplit('=').next().unwrap().to_string(); + assert_ne!(sig(&once), ""); + // The same path signed under one rule and the other differs, scope aside: computed here by hand. + let path = "/device/arn%3Aaws%3Abraket%3A%3A%3Adevice%2Fquantum-simulator%2Famazon%2Fsv1"; // allow-name + let headers = vec![("host".to_string(), "svc.example".to_string()), ("x-amz-content-sha256".to_string(), hex_sha256(b"")), ("x-amz-date".to_string(), "20261105T000000Z".to_string())]; + let by_hand = |uri: &str, service: &str| signature("GET", uri, "", &headers, &hex_sha256(b""), keys().secret.expose(), "20261105T000000Z", "us-east-1", service); + assert_eq!(sig(&once), by_hand(&path.replace('%', "%25"), SERVICE)); + assert_eq!(sig(&twice), by_hand(path, STORAGE)); + } + + #[test] + fn a_credential_reads_as_id_secret_and_token() { + let k = Keys::parse(&Secret::new("id:se/cret:to:ken")).unwrap(); + assert_eq!((k.id.as_str(), k.secret.expose(), k.token.as_ref().map(|t| t.expose())), ("id", "se/cret", Some("to:ken"))); + assert!(Keys::parse(&Secret::new("only-an-id")).is_err()); + assert!(Keys::parse(&Secret::new(":secret")).is_err()); + } + + #[test] + fn an_angle_is_an_exact_multiple_of_pi() { + let d = |m, k| Dyadic::new(m, k).unwrap(); + assert_eq!(angle(Dyadic::ZERO), "0"); + assert_eq!(angle(d(1, 1)), "1*pi/1"); + assert_eq!(angle(d(1, 2)), "1*pi/2"); + assert_eq!(angle(d(3, 3)), "3*pi/4"); + assert_eq!(angle(d(1, 32)), "1*pi/2147483648"); + } + + fn capabilities(native: Value, connectivity: Value, cost: Value) -> String { + json!({ + "paradigm": {"qubitCount": 3, "nativeGateSet": native, "connectivity": connectivity}, + "service": {"deviceCost": cost, "shotsRange": [1, 20000], "updatedAt": "2026-11-05T00:00:00Z"}, + }) + .to_string() + } + + fn device(kind: &str, caps: String) -> Value { + json!({"deviceName": "D", "providerName": "P", "deviceStatus": "ONLINE", "deviceType": kind, "deviceCapabilities": caps}) + } + + #[test] + fn a_processor_reads_as_its_basis_labels_and_price() { + let graph = json!({"fullyConnected": false, "connectivityGraph": {"1": ["2"], "2": ["1", "5"], "5": ["2"]}}); + let c = read_device(&device("QPU", capabilities(json!(["PRx", "CZ"]), graph, json!({"price": "0.00145", "unit": "shot"})))).unwrap(); + assert_eq!(c.basis, Basis::PrxCz); + assert_eq!(c.labels, vec![1, 2, 5]); + assert!(c.coupling.connected(0, 1) && c.coupling.connected(1, 2) && !c.coupling.connected(0, 2)); + assert_eq!(c.price, Some(("0.00145".to_string(), "shot".to_string()))); + assert_eq!(c.max_shots, Some(20000)); + let full = read_device(&device("QPU", capabilities(json!(["GPI", "GPI2", "ZZ"]), json!({"fullyConnected": true}), json!({"price": 0.08, "unit": "shot"})))).unwrap(); + assert_eq!((full.basis, full.labels), (Basis::GpiZz, vec![0, 1, 2])); + // A native set no basis covers is refused with what it runs, not compiled by someone else. + let other = read_device(&device("QPU", capabilities(json!(["rx", "rz", "iswap"]), json!({"fullyConnected": true}), json!({"price": "1", "unit": "shot"})))); + assert!(matches!(other, Err(QpuError::Unsupported(m)) if m.contains("iswap"))); + } + + fn program(basis: Basis, gates: Vec, final_layout: Vec) -> NativeProgram { + let n_qubits = final_layout.len() as u8; + NativeProgram { basis, n_qubits, layout: final_layout.clone(), final_layout, gates, residual: Default::default(), swaps: 0 } + } + + #[test] + fn a_processor_gets_a_verbatim_box_on_its_own_qubit_names() { + let graph = json!({"fullyConnected": false, "connectivityGraph": {"1": ["2"], "2": ["1"]}}); + let caps = read_device(&device("QPU", capabilities(json!(["prx", "cz"]), graph, json!({"price": "1", "unit": "shot"})))).unwrap(); + let d = |m, k| Dyadic::new(m, k).unwrap(); + let p = program(Basis::PrxCz, vec![NativeGate::Prx(0, d(1, 2), d(1, 3)), NativeGate::Cz(0, 1)], vec![0, 1]); + assert_eq!(openqasm(&p, &caps).unwrap(), "OPENQASM 3.0;\nbit[2] b;\n#pragma braket verbatim\nbox {\nprx(1*pi/2, 1*pi/4) $1;\ncz $1, $2;\n}\nb[0] = measure $1;\nb[1] = measure $2;\n"); // allow-name + } + + #[test] + fn a_device_of_rx_rz_cz_runs_the_basis_with_sx_and_x_as_rx() { + let graph = json!({"fullyConnected": false, "connectivityGraph": {"0": ["1"], "1": ["0"]}}); + let caps = read_device(&device("QPU", capabilities(json!(["rx", "rz", "cz", "barrier"]), graph, json!({"price": "0.000425", "unit": "shot"})))).unwrap(); + assert_eq!(caps.basis, Basis::RzSxXCz); + let d = Dyadic::new(1, 3).unwrap(); + let p = program(Basis::RzSxXCz, vec![NativeGate::Sx(0), NativeGate::X(1), NativeGate::Rz(0, d), NativeGate::Cz(0, 1)], vec![0, 1]); + assert_eq!(openqasm(&p, &caps).unwrap(), "OPENQASM 3.0;\nbit[2] b;\n#pragma braket verbatim\nbox {\nrx(pi/2) $0;\nrx(pi) $1;\nrz(1*pi/4) $0;\ncz $0, $1;\n}\nb[0] = measure $0;\nb[1] = measure $1;\n"); // allow-name + } + + #[test] + fn a_simulator_takes_a_processor_s_basis_and_a_processor_refuses_one() { + let simulator = |_: &Request| Response::json(200, &device("SIMULATOR", capabilities(json!([]), json!({}), json!({"price": "0.075", "unit": "minute"})))); + let c = CloudTasks::new(Scripted::new(simulator), keys(), "us-west-1", "arn:device", "b-c").unwrap().with_basis(Basis::GpiZz); + assert_eq!(c.device().unwrap().basis, Basis::GpiZz); + let d = Dyadic::new(1, 2).unwrap(); + let p = program(Basis::GpiZz, vec![NativeGate::Gpi2(0, d), NativeGate::Zz(0, 1, d)], vec![0, 1]); + assert!(c.source(&p).unwrap().contains("gpi2(1*pi/2) q[0];\nzz(1*pi/2) q[0], q[1];")); + let processor = |_: &Request| Response::json(200, &device("QPU", capabilities(json!(["prx", "cz"]), json!({"fullyConnected": true}), json!({"price": "1", "unit": "shot"})))); + let c = CloudTasks::new(Scripted::new(processor), keys(), "us-west-1", "arn:device", "b-c").unwrap().with_basis(Basis::GpiZz); + assert!(matches!(c.device(), Err(QpuError::Config(m)) if m.contains("for a simulator"))); + } + + #[test] + fn a_simulator_gets_a_register_as_wide_as_the_program() { + let caps = read_device(&device("SIMULATOR", capabilities(json!([]), json!({}), json!({"price": "0.075", "unit": "minute"})))).unwrap(); + let p = program(Basis::RzSxXCz, vec![NativeGate::Sx(0), NativeGate::Cz(0, 1)], vec![0, 1]); + assert_eq!(openqasm(&p, &caps).unwrap(), "OPENQASM 3.0;\nbit[2] b;\nqubit[2] q;\nv q[0];\ncz q[0], q[1];\nb[0] = measure q[0];\nb[1] = measure q[1];\n"); + // A qubit no gate touches gets an identity, since the service refuses a register with one idle. + let idle = program(Basis::RzSxXCz, vec![NativeGate::X(1)], vec![0, 1, 2]); + assert_eq!(openqasm(&idle, &caps).unwrap(), "OPENQASM 3.0;\nbit[3] b;\nqubit[3] q;\nx q[1];\ni q[0];\ni q[2];\nb[0] = measure q[0];\nb[1] = measure q[1];\nb[2] = measure q[2];\n"); + } + + #[test] + fn counts_come_back_on_logical_qubits_from_shots_or_probabilities() { + // Logical 0 ends on label 2, logical 1 on label 1. + let ends = [2, 1]; + let rows = json!({"measuredQubits": [1, 2], "measurements": [[0, 1], [0, 1], [1, 0]]}); + let probs = json!({"measuredQubits": [1, 2], "measurementProbabilities": {"01": 0.75, "10": 0.25}}); + let (by_shots, from_rows) = read_counts(&rows, &ends, 2, 3).unwrap(); + let (by_probs, from_probs) = read_counts(&probs, &ends, 2, 4).unwrap(); + // Rows are shot records; counts made from probabilities say so. + assert_eq!((from_rows, from_probs), (CountsSource::Shots, CountsSource::Probabilities)); + // Label 2 set is logical 0 set: outcome 1 (bit 0). Label 1 set is logical 1 set: outcome 2. + let get = |c: &Counts, k: usize| c[k]; + assert_eq!((get(&by_shots, 1), get(&by_shots, 2)), (2, 1)); + assert_eq!((get(&by_probs, 1), get(&by_probs, 2)), (3, 1)); + // The order of measuredQubits is the service's, and the labels say which is which. + let swapped = json!({"measuredQubits": [2, 1], "measurements": [[1, 0], [1, 0], [0, 1]]}); + assert_eq!(read_counts(&swapped, &ends, 2, 3).unwrap().0, by_shots); + } + + #[test] + fn a_qubit_the_program_ended_on_must_have_been_measured_and_a_row_must_hold_bits() { + let missing = read_counts(&json!({"measuredQubits": [1], "measurements": [[1]]}), &[1, 2], 2, 1); + assert!(matches!(missing, Err(QpuError::Counts(m)) if m.contains("qubit 2"))); + let not_bits = read_counts(&json!({"measuredQubits": [1], "measurements": [[2]]}), &[1], 1, 1); + assert!(matches!(not_bits, Err(QpuError::Counts(m)) if m.contains("other than bits"))); + } + + #[test] + fn a_client_with_no_record_of_a_job_on_a_processor_does_not_guess_its_qubits() { + let answer = |r: &Request| { + if r.url.contains("/device/") { Response::json(200, &line(4)) } else { Response::json(200, &json!({"status": "COMPLETED", "outputS3Directory": "runs/t"})) } + }; + let c = CloudTasks::new(Scripted::new(answer), keys(), "us-east-1", "arn:device", "b-c").unwrap(); + let p = program(Basis::PrxCz, vec![], vec![0]); + assert!(matches!(c.counts("arn:task", &p, 1), Err(QpuError::Counts(m)) if m.contains("which qubits"))); + // Nothing was read from the bucket. + assert!(!c.transport.requests().iter().any(|r| r.url.ends_with("results.json"))); + } + + #[test] + fn an_estimate_says_what_the_service_does_not_state() { + let answer = |_: &Request| Response::json(200, &device("QPU", capabilities(json!(["prx", "cz"]), json!({"fullyConnected": true}), json!({"price": "0.00145", "unit": "shot"})))); + let c = CloudTasks::new(Scripted::new(answer), keys(), "eu-north-1", "arn:device", "bucket").unwrap(); + let p = program(Basis::PrxCz, vec![], vec![0]); + let e = c.estimate(&p, 1000).unwrap().unwrap(); + assert!(!e.free); + assert_eq!(e.cost.unwrap().amount, "1.750000"); + assert!(e.note.unwrap().contains("fee per task of 0.30 USD")); + } + + #[test] + fn a_submission_names_the_persons_own_bucket_and_never_carries_their_secret() { + let answer = |r: &Request| { + if r.method.as_str() == "POST" { + Response::json(201, &json!({"quantumTaskArn": "arn:task"})) + } else { + Response::json(200, &device("SIMULATOR", capabilities(json!([]), json!({}), json!({"price": "0.075", "unit": "minute"})))) + } + }; + let c = CloudTasks::new(Scripted::new(answer), keys(), "us-east-1", "arn:device", "my-bucket").unwrap().with_prefix("runs"); + let p = program(Basis::RzSxXCz, vec![NativeGate::X(0)], vec![0]); + assert_eq!(c.submit(&p, 100).unwrap(), "arn:task"); + let sent = c.transport.requests(); + let post = sent.iter().find(|r| r.method.as_str() == "POST").unwrap(); + let body: Value = serde_json::from_slice(post.body.as_ref().unwrap()).unwrap(); + assert_eq!((body["outputS3Bucket"].as_str(), body["outputS3KeyPrefix"].as_str(), body["shots"].as_u64()), (Some("my-bucket"), Some("runs"), Some(100))); + assert!(body["action"].as_str().unwrap().contains("x q[0];")); + // Nothing of the keys is in the body, whose bytes a request's own debug text does not show as text. + for r in &sent { + let text = carried(r); + assert!(!text.contains(keys().secret.expose()), "{text}"); + assert!(r.header_value("Authorization").unwrap().starts_with("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/")); // allow-name + } + } + + #[test] + fn a_failed_task_says_why_and_a_cancel_is_a_signed_put() { + let answer = |r: &Request| match r.method.as_str() { + "PUT" => Response::json(200, &json!({"cancellationStatus": "CANCELLING"})), + _ => Response::json(200, &json!({"status": "FAILED", "failureReason": "the device was offline"})), + }; + let c = CloudTasks::new(Scripted::new(answer), keys(), "us-east-1", "arn:device", "b-c").unwrap(); + assert_eq!(c.status("arn:task").unwrap(), JobStatus::Ended { status: "failed".into(), reason: "the device was offline".into() }); + c.cancel("arn:task").unwrap(); + let sent = c.transport.requests(); + assert!(sent.last().unwrap().url.ends_with("/quantum-task/arn%3Atask/cancel")); + } + + /// A processor of `n` qubits in a line, labelled 0 to `n - 1`. + fn line(n: u64) -> Value { + let mut graph = serde_json::Map::new(); + for l in 0..n { + let near: Vec = [l.checked_sub(1), Some(l + 1).filter(|&x| x < n)].into_iter().flatten().map(|x| x.to_string()).collect(); + graph.insert(l.to_string(), json!(near)); + } + let caps = json!({"paradigm": {"qubitCount": n, "nativeGateSet": ["prx", "cz"], "connectivity": {"fullyConnected": false, "connectivityGraph": graph}}, "service": {"deviceCost": {"price": "1", "unit": "shot"}}}); + device("QPU", caps.to_string()) + } + + #[test] + fn a_program_on_a_qubit_past_the_sixty_fourth_is_read_back() { + let answer = |r: &Request| { + if r.url.ends_with("results.json") { + Response::json(200, &json!({"measuredQubits": [65, 70], "measurements": [[1, 0], [1, 0], [0, 1]]})) + } else if r.url.contains("/device/") { + Response::json(200, &line(72)) + } else { + Response::json(200, &json!({"status": "COMPLETED", "outputS3Directory": "runs/t"})) + } + }; + let c = CloudTasks::new(Scripted::new(answer), keys(), "us-east-1", "arn:device", "b-c").unwrap(); + c.device().unwrap(); + let p = program(Basis::PrxCz, vec![], vec![65, 70]); + let names = c.qubit_names(&p).unwrap(); + let (counts, _, _) = c.counts_stated("arn:task", &p, 3, names.as_ref()).unwrap(); + assert_eq!((counts[1], counts[2]), (2, 1)); + } + + #[test] + fn a_dotted_bucket_is_read_by_path_where_its_host_has_no_certificate() { + let answer = |r: &Request| { + if r.url.ends_with("results.json") { + Response::json(200, &json!({"measuredQubits": [0], "measurements": [[1]]})) + } else if r.url.contains("/device/") { + Response::json(200, &line(2)) + } else { + Response::json(200, &json!({"status": "COMPLETED", "outputS3Directory": "runs/t", "outputS3Bucket": "amazon-braket-a.b"})) // allow-name: the prefix the service requires + } + }; + let c = CloudTasks::new(Scripted::new(answer), keys(), "us-east-1", "arn:device", "amazon-braket-a.b").unwrap(); // allow-name + c.device().unwrap(); + let p = program(Basis::PrxCz, vec![], vec![0]); + c.counts_stated("arn:task", &p, 1, c.qubit_names(&p).unwrap().as_ref()).unwrap(); + let sent = c.transport.requests(); + let read = sent.iter().find(|r| r.url.ends_with("results.json")).unwrap(); + assert_eq!(read.url, "https://s3.us-east-1.amazonaws.com/amazon-braket-a.b/runs/t/results.json"); // allow-name: the service's endpoint + assert!(read.header_value("Authorization").unwrap().contains("/us-east-1/s3/aws4_request")); + } + + #[test] + fn results_are_read_from_the_bucket_the_task_names() { + // The entry was pointed at another bucket while the task waited. + let answer = |r: &Request| { + if r.url.ends_with("results.json") { + Response::json(200, &json!({"measuredQubits": [0], "measurements": [[1]]})) + } else if r.url.contains("/device/") { + Response::json(200, &line(2)) + } else { + Response::json(200, &json!({"status": "COMPLETED", "outputS3Directory": "runs/t", "outputS3Bucket": "the-first-one"})) + } + }; + let c = CloudTasks::new(Scripted::new(answer), keys(), "us-east-1", "arn:device", "another-one").unwrap(); + c.device().unwrap(); + let p = program(Basis::PrxCz, vec![], vec![0]); + c.counts_stated("arn:task", &p, 1, c.qubit_names(&p).unwrap().as_ref()).unwrap(); + assert!(c.transport.requests().iter().any(|r| r.url == "https://the-first-one.s3.us-east-1.amazonaws.com/runs/t/results.json")); // allow-name: the service's endpoint + } + + #[test] + fn temporary_keys_send_their_token_and_sign_it() { + let token = "IQoJb3JpZ2luX2VjEXAMPLETOKEN"; + let k = Keys::new("AKIDEXAMPLE", Secret::new("wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"), Some(Secret::new(token))); + let r = sign(Request::get("https://svc.example/x"), SERVICE, "us-east-1", &k, "20261105T000000Z".into()); + assert_eq!(r.header_value("x-amz-security-token"), Some(token)); + let auth = r.header_value("Authorization").unwrap(); + assert!(auth.contains("SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token,"), "{auth}"); + // The signature is over the token: by hand, with it among the headers. + let headers = vec![("host".to_string(), "svc.example".to_string()), ("x-amz-content-sha256".to_string(), hex_sha256(b"")), ("x-amz-date".to_string(), "20261105T000000Z".to_string()), ("x-amz-security-token".to_string(), token.to_string())]; + let by_hand = signature("GET", "/x", "", &headers, &hex_sha256(b""), k.secret.expose(), "20261105T000000Z", "us-east-1", SERVICE); + assert!(auth.ends_with(&format!("Signature={by_hand}")), "{auth}"); + } + + /// Everything a request carries, its body as the text it is. + fn carried(r: &Request) -> String { + format!("{} {:?} {}", r.url, r.headers, String::from_utf8_lossy(r.body.as_deref().unwrap_or_default())) + } + + #[test] + fn the_look_for_a_secret_reads_a_request_s_body() { + let r = Request::post("https://svc.example/x", b"{\"a\":\"hidden\"}".to_vec()); + assert!(carried(&r).contains("hidden")); + assert!(!format!("{r:?}").contains("hidden"), "a request's debug text shows its body as numbers, so it cannot be the check"); + } + + /// A client reads a task it did not submit itself through nothing it can + /// trust: reading the device, as every sampler does first, is not a record. + #[test] + fn a_client_that_did_not_submit_a_task_does_not_read_it_through_today_s_device() { + let answer = |r: &Request| { + if r.url.contains("/device/") { Response::json(200, &line(4)) } else { Response::json(200, &json!({"status": "COMPLETED", "outputS3Directory": "runs/t", "measuredQubits": [0], "measurements": [[1]]})) } + }; + let c = CloudTasks::new(Scripted::new(answer), keys(), "us-east-1", "arn:device", "b-c").unwrap(); + c.device().unwrap(); + let p = program(Basis::PrxCz, vec![], vec![0]); + assert!(matches!(c.counts("arn:task", &p, 1), Err(QpuError::Counts(m)) if m.contains("which qubits"))); + assert!(!c.transport.requests().iter().any(|r| r.url.ends_with("results.json"))); + } + + /// A task is asked after where it is: its region is in its id, whatever + /// the entry says by now. + #[test] + fn a_task_is_asked_after_in_the_region_its_id_names() { + let id = "arn:aws:braket:us-west-1:123456789012:quantum-task/0f1e2d3c"; // allow-name: a task's id, as the service gives it + let answer = |r: &Request| { + if r.url.ends_with("results.json") { + Response::json(200, &json!({"measuredQubits": [0], "measurements": [[1]]})) + } else if r.url.contains("/device/") { + Response::json(200, &line(2)) + } else { + Response::json(200, &json!({"status": "COMPLETED", "outputS3Bucket": "my-results", "outputS3Directory": "runs/t"})) + } + }; + let c = CloudTasks::new(Scripted::new(answer), keys(), "eu-north-1", "arn:device", "my-results").unwrap(); + let mut names = QubitNames::new(); + names.insert(0, "0".to_string()); + c.status(id).unwrap(); + c.counts_stated(id, &program(Basis::PrxCz, vec![], vec![0]), 1, Some(&names)).unwrap(); + let _ = c.cancel(id); + for r in c.transport.requests().iter().filter(|r| !r.url.contains("/device/")) { + let host = r.url.split('/').nth(2).unwrap(); + assert!(host.ends_with(".us-west-1.amazonaws.com"), "{}", r.url); // allow-name: the service's endpoint + assert!(r.header_value("Authorization").unwrap().contains("/us-west-1/"), "{}", r.url); + } + } + + /// The published signing test suite (its v4 vectors, version 2015-08-30, + /// each request with the key, region, service and time it names), signed + /// by the code that signs every request here: the signature it publishes. + /// Its paths are as a request line writes them, unencoded, and its canonical + /// path is that path encoded once: the task service's rule, which encodes + /// once more the path a URL holds. Storage's rule takes the path as sent, + /// so the same path, sent encoded, gives the same signature. + #[test] + fn signing_gives_the_published_test_suite_s_signatures() { + let k = Keys::new("AKIDEXAMPLE", Secret::new("wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"), None); + let at = "20150830T123600Z".to_string(); + let sig = |r: &Request| r.header_value("Authorization").unwrap().rsplit("Signature=").next().unwrap().to_string(); + let host = "https://example.amazonaws.com"; // allow-name: the suite's host + for (path, sent, want) in [ + ("/", "/", "5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31"), + ("/\u{1234}", "/%E1%88%B4", "8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85"), + ("/example space/", "/example%20space/", "652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741"), + ("/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", "07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f"), + ] { + let task_rule = sign_with(Request::get(format!("{host}{path}")), "service", "us-east-1", &k, at.clone(), false, false); + assert_eq!(sig(&task_rule), want, "{path}, encoded once more"); + let as_sent = sign_with(Request::get(format!("{host}{sent}")), "service", "us-east-1", &k, at.clone(), false, true); + assert_eq!(sig(&as_sent), want, "{sent}, as sent"); + assert!(task_rule.header_value("Authorization").unwrap().contains("SignedHeaders=host;x-amz-date,")); + } + // A body, its hash a signed header, and the headers it carries. + let post = Request::post(format!("{host}/"), b"Param1=value1".to_vec()).header("Content-Type", "application/x-www-form-urlencoded").header("Content-Length", "13"); + let signed = sign_with(post, "service", "us-east-1", &k, at.clone(), true, false); + assert_eq!(sig(&signed), "d3875051da38690788ef43de4db0d8f280229d82040bfac253562e56c3f20e0b"); + assert!(signed.header_value("Authorization").unwrap().contains("SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date,")); + // The task service's path, encoded once more: a device's name holds ':' and '/'. + let url = format!("{host}/device/{}", encode("a:b/c")); + assert!(url.ends_with("/device/a%3Ab%2Fc")); + let by_rule = sign(Request::get(url.clone()), SERVICE, "us-east-1", &k, at.clone()); + let by_hand = sign_with(Request::get(format!("{host}/device/a%3Ab%2Fc")), SERVICE, "us-east-1", &k, at.clone(), true, false); + assert_eq!(sig(&by_rule), sig(&by_hand)); + let twice = sign_with(Request::get(format!("{host}/device/a%253Ab%252Fc")), SERVICE, "us-east-1", &k, at, true, true); + assert_eq!(sig(&by_rule), sig(&twice), "the canonical path is the name encoded twice"); + } + + /// The conformance check says a simulator's estimate is the least it can + /// cost, in what it ran and in why it ran nothing. + #[test] + fn the_conformance_check_says_a_minimum_is_one() { + let simulator = |_: &Request| Response::json(200, &device("SIMULATOR", capabilities(json!([]), json!({}), json!({"price": "0.075", "unit": "minute"})))); + let c = CloudTasks::new(Scripted::new(simulator), keys(), "us-west-1", "arn:device", "b-c").unwrap(); + let t = crate::job::Target::new("tasks", crate::registry::Kind::CloudTasks, Box::new(c), None); + let text = crate::conformance::check_builtin(&t, false, crate::conformance::WAIT).text(); + let lines: Vec<&str> = text.lines().filter(|l| l.contains("0.003750 USD")).collect(); + assert!(!lines.is_empty() && lines.iter().all(|l| l.contains("at least 0.003750 USD")), "{text}"); + assert!(text.lines().any(|l| l.contains("allow the spend") && l.contains("at least")), "{text}"); + } +} diff --git a/wai/wai-quantum-qpu/src/conformance.rs b/wai/wai-quantum-qpu/src/conformance.rs index 4e51d7244..e42f76f22 100644 --- a/wai/wai-quantum-qpu/src/conformance.rs +++ b/wai/wai-quantum-qpu/src/conformance.rs @@ -347,7 +347,7 @@ fn common(r: &mut Report, p: &dyn Provider, sampler: &QpuSampler<&dyn Provider>, Ok(Some(e)) => { if !e.free { all_free = false; - costs.push(e.cost.map(|c| format!("{} {}", c.amount, c.currency)).unwrap_or_default()); + costs.push(e.cost.map(|c| format!("{}{} {}", if e.at_least { "at least " } else { "" }, c.amount, c.currency)).unwrap_or_default()); } r.push(format!("{name}: estimated"), Outcome::Pass, if e.free { "free".to_string() } else { costs.last().cloned().unwrap_or_default() }); } diff --git a/wai/wai-quantum-qpu/src/job.rs b/wai/wai-quantum-qpu/src/job.rs index b875c98de..5683d4aad 100644 --- a/wai/wai-quantum-qpu/src/job.rs +++ b/wai/wai-quantum-qpu/src/job.rs @@ -35,7 +35,7 @@ use crate::open_provider::PROTOCOL; use crate::registry::Kind; use crate::ledger::{Ledger, Ticket}; -use crate::{Billing, Details, Device, Estimate, JobInfo, JobRecord, JobStatus, Provider, QpuError, QpuSampler, Secret, Wait, basis_label}; +use crate::{Billing, CountsSource, Details, Device, Estimate, JobInfo, JobRecord, JobStatus, Provider, QpuError, QpuSampler, Secret, Stated, Wait, basis_label}; use serde_json::{Value, json}; use std::time::Duration; use wai_quantum::ed25519_dalek::SigningKey; @@ -55,19 +55,26 @@ pub struct Target { pub name: String, pub kind: Kind, pub provider: Box, - secret: Option, + secrets: Vec, } impl Target { /// `provider` under the person's name for it, with the credential it was /// given (kept only to take out of messages). pub fn new(name: impl Into, kind: Kind, provider: Box, secret: Option) -> Target { - Target { name: name.into(), kind, provider, secret } + Target { name: name.into(), kind, provider, secrets: secret.into_iter().collect() } + } + + /// Takes these out of messages too: the parts of a credential that is + /// never sent or quoted whole. + pub fn with_secrets(mut self, parts: impl IntoIterator) -> Target { + self.secrets.extend(parts); + self } /// `text` with the credential taken out. pub fn redact(&self, text: &str) -> String { - self.secret.as_ref().map_or_else(|| text.to_string(), |s| s.redact(text)) + self.secrets.iter().fold(text.to_string(), |text, s| s.redact(&text)) } fn fail(&self, e: impl std::fmt::Display) -> JobError { @@ -115,13 +122,17 @@ fn provider_json(t: &Target, device: &Device, details: &Details) -> Value { }) } -/// The program sent, as a result names it. +/// The program sent, as a result names it: its OpenQASM 3 text (`qasm3`) and +/// that text's BLAKE3 (`program`), so that anyone holding the receipt can +/// check the one against the other and read the program the device was sent. pub fn compiled_json(p: &NativeProgram) -> Value { let two = p.gates.iter().filter(|g| matches!(g, NativeGate::Cz(..) | NativeGate::Zz(..))).count(); + let text = p.qasm3(); json!({ "basis": basis_label(p.basis), - "program": hex(blake3::hash(p.qasm3().as_bytes()).as_bytes()), - "hash": "BLAKE3 of the program's OpenQASM 3 text (wai-quantum-qpu NativeProgram::qasm3)", + "program": hex(blake3::hash(text.as_bytes()).as_bytes()), + "hash": "BLAKE3 of the program's OpenQASM 3 text, qasm3", + "qasm3": text, "native_gates": p.gates.len(), "two_qubit_gates": two, "swaps": p.swaps, @@ -131,6 +142,28 @@ pub fn compiled_json(p: &NativeProgram) -> Value { }) } +/// [`compiled_json`], with the device's names for its qubits where its client +/// sends it under them: `qasm3` is on indices of the device's coupling map, and +/// these say which of its qubits each was. +fn named_compiled(p: &NativeProgram, names: Option<&crate::QubitNames>) -> Value { + let mut c = compiled_json(p); + if let Some(names) = names { + c["qubit_names"] = json!(names); + } + c +} + +/// What is sealed of a job, from its record. A record written before results +/// carried the program's text has its program beside it, the text's hash is +/// the hash it recorded, and it is added here. +fn sealing_from_record(recorded: &Value, program: &NativeProgram) -> Value { + let mut sealing = recorded.clone(); + if sealing["compiled"].is_object() && sealing["compiled"].get("qasm3").is_none() { + sealing["compiled"]["qasm3"] = json!(program.qasm3()); + } + sealing +} + fn billing_json(d: &Details) -> Value { let mut v = match &d.billing { Billing::Free => json!({ "free": true }), @@ -160,6 +193,9 @@ fn estimate_json(e: &Result, String>) -> Value { if let Some(n) = &e.note { v["note"] = json!(n); } + if e.at_least { + v["at_least"] = json!(true); + } v } Ok(None) => json!({ "unknown": "the provider gives no estimate" }), @@ -209,6 +245,9 @@ pub struct Plan { device: Device, details: Details, estimate: Result, String>, + /// The device's names for the program's qubits, where its client sends it + /// under them. + names: Option, } impl Plan { @@ -226,6 +265,7 @@ fn plan_with(t: &Target, sampler: &QpuSampler<&dyn Provider>, request: &Value) - let device = sampler.device().clone(); let details = t.provider.details().map_err(|e| t.fail(e))?; let (program, placement) = compile_for(sampler, &details, &read).map_err(|e| t.fail(e))?; + let names = t.provider.qubit_names(&program).map_err(|e| t.fail(e))?; let estimate = t.provider.estimate(&program, read.shots).map_err(|e| t.redact(&e.to_string())); let free = match &estimate { Ok(Some(e)) => e.free, @@ -236,7 +276,7 @@ fn plan_with(t: &Target, sampler: &QpuSampler<&dyn Provider>, request: &Value) - "free: it runs without a confirmation".to_string() } else { let cost = match &estimate { - Ok(Some(Estimate { cost: Some(c), unit, .. })) => format!("the provider estimates {} {}{}", c.amount, c.currency, unit.as_ref().map(|u| format!(", billed by the {u}")).unwrap_or_default()), + Ok(Some(Estimate { cost: Some(c), unit, at_least, .. })) => format!("the provider estimates {}{} {}{}", if *at_least { "at least " } else { "" }, c.amount, c.currency, unit.as_ref().map(|u| format!(", billed by the {u}")).unwrap_or_default()), _ => match &details.billing { Billing::Priced { unit, price, currency } => format!("the provider gives no estimate; it bills {price} {currency} a {unit}"), Billing::Unknown(why) => format!("its cost is unknown ({why})"), @@ -254,14 +294,14 @@ fn plan_with(t: &Target, sampler: &QpuSampler<&dyn Provider>, request: &Value) - "method": "provider", "qubits": read.program.n_qubits, "shots": read.shots, - "compile": compiled_json(&program), + "compile": named_compiled(&program, names.as_ref()), "placement": placement, }, "estimate": estimate_json(&estimate), "free": free, "confirm": confirm, }); - Ok(Plan { json, free, read, program, placement, device, details, estimate }) + Ok(Plan { json, free, read, program, placement, device, details, estimate, names }) } /// The dry run: the plan for `request` on `t`, with nothing submitted. @@ -293,12 +333,17 @@ fn refuse_unconfirmed(t: &Target, p: &Plan, confirmed: bool) -> Result<(), JobEr /// when its result is collected. fn sealing_of(t: &Target, p: &Plan, confirmed: bool) -> Value { let protocol = p.json["provider"]["protocol"].as_str().unwrap_or(PROTOCOL).to_string(); - let mut compiled = compiled_json(&p.program); + let mut compiled = named_compiled(&p.program, p.names.as_ref()); compiled["placement"] = p.placement.clone(); + // A simulator the provider states is approximate (§5.1); a processor's + // counts never are. + let approximate = p.details.describe.get("approximate") == Some(&Value::Bool(true)) && p.device.provenance.label() != "silicon"; json!({ "provider": provider_json(t, &p.device, &p.details), "calibration": p.details.calibration, "compiled": compiled, + "execution": t.provider.execution(&p.program), + "approximate": approximate, "free": p.free, "estimate": estimate_json(&p.estimate), "confirmed": confirmed && !p.free, @@ -306,9 +351,14 @@ fn sealing_of(t: &Target, p: &Plan, confirmed: bool) -> Value { }) } -/// The counts of job `id`, as a sealed job. +/// The counts of job `id`, as a sealed job. Where its counts come from is the +/// client's word, or, when it says nothing, the provider's own counts; how +/// the device was asked to run the program is the provider's word when it +/// states it with the counts, else the client's, recorded when the job was +/// sent, else no one's (a job recorded before clients said, which the verifier +/// takes on any kind: §8.4). #[allow(clippy::too_many_arguments)] -fn sealed(t: &Target, request: &Value, read: &provider::Request, sealing: &Value, id: &str, polls: u32, counts: Counts, info: &JobInfo, key: &SigningKey) -> Result { +fn sealed(t: &Target, request: &Value, read: &provider::Request, sealing: &Value, id: &str, polls: u32, counts: Counts, info: &JobInfo, stated: &Stated, key: &SigningKey) -> Result { let charged = info.charged.as_ref().map(|c| json!({ "amount": c.amount, "currency": c.currency })); let measured = provider::Measured { counts, @@ -319,8 +369,17 @@ fn sealed(t: &Target, request: &Value, read: &provider::Request, sealing: &Value cost: json!({ "free": sealing["free"], "estimate": sealing["estimate"], "charged": charged, "confirmed": sealing["confirmed"] }), energy: energy_json(info.service_kwh), }; - let result = provider::result(read, measured).map_err(|e| t.fail(e))?; - Ok(provider::seal(request, result, key, sealing["why"].as_str().unwrap_or_default())) + let execution = stated.execution.clone().or_else(|| sealing.get("execution").filter(|e| e.is_object()).cloned()).unwrap_or_else(provider::unstated_execution); + let statement = provider::Statement::new(stated.counts_source.unwrap_or(CountsSource::VendorCounts), execution).approximate(sealing["approximate"] == true); + let result = provider::result_stated(read, measured, &statement).map_err(|e| t.fail(e))?; + let body = provider::seal(request, result, key, sealing["why"].as_str().unwrap_or_default()); + // A provider's word reaches the seal, and a host gives no receipt its own + // verifier refuses, whatever a provider answered. + let own = wai_quantum::engine::job::check(&body); + if !own.valid() { + return Err(t.fail(format!("no receipt is given: this host's verifier would refuse it ({})", own.reasons.join(", ")))); + } + Ok(body) } /// Runs `request` on `t`: refused, with the plan, when it is not free and @@ -333,9 +392,9 @@ pub fn run(t: &Target, request: &Value, confirmed: bool, key: &SigningKey, wait: let p = plan_with(t, &sampler, request)?; refuse_unconfirmed(t, &p, confirmed)?; let sealing = sealing_of(t, &p, confirmed); - let (counts, record): (_, JobRecord) = sampler.run_compiled(&p.read.circuit, &p.program, p.read.shots, p.read.seed).map_err(|e| t.fail(e))?; + let (counts, record, stated): (_, JobRecord, _) = sampler.run_compiled_stated(&p.read.circuit, &p.program, p.read.shots, p.read.seed).map_err(|e| t.fail(e))?; let info = JobInfo { service_kwh: record.service_kwh, calibration: record.calibration, charged: record.charged }; - sealed(t, request, &p.read, &sealing, &record.id, record.polls, counts, &info, key) + sealed(t, request, &p.read, &sealing, &record.id, record.polls, counts, &info, &stated, key) } /// What came of asking after a recorded job. Each holds the job in the form @@ -381,7 +440,7 @@ pub fn start(t: &Target, request: &Value, confirmed: bool, ledger: &Ledger) -> R let sampler = QpuSampler::new(&*t.provider).map_err(|e| t.fail(e))?; let p = plan_with(t, &sampler, request)?; refuse_unconfirmed(t, &p, confirmed)?; - let mut ticket = Ticket::recording(&t.name, request, &p.program, p.read.shots, p.read.seed, sealing_of(t, &p, confirmed)); + let mut ticket = Ticket::recording(&t.name, request, &p.program, p.read.shots, p.read.seed, sealing_of(t, &p, confirmed), p.names.as_ref()); ledger.put(&ticket).map_err(|e| JobError::Failed(format!("nothing was sent: the job could not be recorded first ({e})")))?; match t.provider.submit_seeded(&p.program, p.read.shots, p.read.seed) { Ok(id) => { @@ -424,8 +483,10 @@ fn ask(t: &Target, ticket: &mut Ticket, key: &SigningKey, ledger: &Ledger) -> Re Ok(JobStatus::Done) => { let read = provider::read(ticket.request()).map_err(|e| t.fail(e))?; let program = ticket.program().map_err(|e| t.fail(e))?; - let (counts, info) = t.provider.counts(&id, &program, ticket.shots()).map_err(|e| t.fail(e))?; - let body = sealed(t, ticket.request(), &read, ticket.sealing(), &id, ticket.polls(), counts, &info, key)?; + let names = ticket.qubit_names().map_err(|e| t.fail(e))?; + let (counts, info, stated) = t.provider.counts_stated(&id, &program, ticket.shots(), names.as_ref()).map_err(|e| t.fail(e))?; + let sealing = sealing_from_record(ticket.sealing(), &program); + let body = sealed(t, ticket.request(), &read, &sealing, &id, ticket.polls(), counts, &info, &stated, key)?; ticket.done(body); } Ok(JobStatus::Ended { status, reason }) => { @@ -706,7 +767,7 @@ mod tests { let (t, ops, _) = watched(&Arc::new(Reference::new(Config::default()).unwrap())); let req = json!({ "qasm": BELL, "provider": "lab" }); let p = plan(&t, &req).unwrap(); - let mut ticket = Ticket::recording("lab", &req, &p.program, 100, None, json!({})); + let mut ticket = Ticket::recording("lab", &req, &p.program, 100, None, json!({}), None); records.ledger().put(&ticket).unwrap(); assert_eq!(ticket.state(), "submitting"); assert!(matches!(check(&t, &mut ticket, &SigningKey::from_bytes(&[4; 32]), &records.ledger()).unwrap(), Progress::Ended(_))); @@ -799,6 +860,88 @@ mod tests { } } + /// The reference with its `result` answers changed by `edit`. + fn answering(edit: fn(&mut Value)) -> Target { + let r = Arc::new(Reference::new(Config::default()).unwrap()); + let ch = Local(move |m: &Value| { + let mut a = r.handle(m, None); + if m["op"] == "result" { + edit(&mut a); + } + a + }); + Target::new("lab", Kind::Process, Box::new(OpenProvider::new(ch)), None) + } + + #[test] + fn what_a_provider_states_of_its_counts_is_sealed_and_a_statement_out_of_form_is_refused() { + let key = SigningKey::from_bytes(&[4; 32]); + let req = json!({ "qasm": BELL, "shots": 300, "provider": "lab" }); + // Counts it rebuilt from probabilities, said so. + let body = run(&answering(|a| a["counts_source"] = json!("probabilities")), &req, false, &key, no_wait(), |_| {}).unwrap(); + assert_eq!(body["result"]["counts_source"], "probabilities"); + assert!(wai_quantum::engine::job::verify(&body).unwrap().0); + // A provider that says nothing: its own counts, and how it ran them no one's word. + let body = run(&answering(|a| { a.as_object_mut().unwrap().remove("counts_source"); a.as_object_mut().unwrap().remove("execution"); }), &req, false, &key, no_wait(), |_| {}).unwrap(); + assert_eq!((body["result"]["counts_source"].as_str(), body["result"]["execution"]["stated_by"].as_str()), (Some("vendor_counts"), Some("nobody")), "{}", body["result"]); + assert!(wai_quantum::engine::job::verify(&body).unwrap().0); + // Statements not of their form are refused, not sealed. + for edit in [(|a: &mut Value| a["counts_source"] = json!("guessed")) as fn(&mut Value), |a| a["execution"]["twirling"] = json!("maybe"), |a| a["execution"]["remapping"] = json!("on")] { + let e = run(&answering(edit), &req, false, &key, no_wait(), |_| {}).unwrap_err(); + assert!(e.to_string().contains("result:"), "{e}"); + } + } + + /// A provider's word reaches the seal, and a host gives no receipt its own + /// verifier would refuse, whatever a provider said. + #[test] + fn a_host_gives_no_receipt_its_verifier_refuses_whatever_a_provider_answers() { + struct Nameless(Box); + impl Provider for Nameless { + fn device(&self) -> Result { + let mut d = self.0.device()?; + d.name = String::new(); + Ok(d) + } + fn submit(&self, program: &NativeProgram, shots: u64) -> Result { + self.0.submit(program, shots) + } + fn status(&self, id: &str) -> Result { + self.0.status(id) + } + fn counts(&self, id: &str, program: &NativeProgram, shots: u64) -> Result<(Counts, JobInfo), crate::QpuError> { + self.0.counts(id, program, shots) + } + } + let inner = target(Config::default()); + let t = Target::new("lab", Kind::Process, Box::new(Nameless(inner.provider)), None); + let req = json!({ "qasm": BELL, "shots": 100, "provider": "lab" }); + let key = SigningKey::from_bytes(&[4; 32]); + let answer = run(&t, &req, true, &key, no_wait(), |_| {}); + match answer { + Ok(body) => panic!("a receipt was given, and its verifier says {:?}", wai_quantum::engine::job::check(&body).reasons), + Err(e) => assert!(e.to_string().contains("provider_members"), "{e}"), + } + } + + #[test] + fn a_job_recorded_before_clients_stated_what_they_sent_says_no_one_stated_it() { + let records = Records::new("unstated"); + let key = SigningKey::from_bytes(&[4; 32]); + let req = json!({ "qasm": BELL, "shots": 100, "provider": "lab" }); + let t = answering(|a| { a.as_object_mut().unwrap().remove("execution"); }); + let mut ticket = start(&t, &req, false, &records.ledger()).unwrap(); + // As an earlier version recorded it: no execution among what it sealed. + let mut sealing = ticket.sealing().clone(); + sealing.as_object_mut().unwrap().remove("execution"); + let mut back = Ticket::recording("lab", &req, &ticket.program().unwrap(), 100, None, sealing, None); + back.submitted(ticket.provider_id().unwrap()); + let Progress::Done(body) = check(&t, &mut back, &key, &records.ledger()).unwrap() else { panic!("not done") }; + assert_eq!(body["result"]["execution"]["stated_by"], "nobody", "{}", body["result"]); + assert!(wai_quantum::engine::job::verify(&body).unwrap().0); + let _ = check(&t, &mut ticket, &key, &records.ledger()); + } + #[test] fn a_free_run_needs_no_confirmation_and_its_receipt_verifies() { let t = target(Config::default()); diff --git a/wai/wai-quantum-qpu/src/ledger.rs b/wai/wai-quantum-qpu/src/ledger.rs index 8f64aa597..856dd9ca0 100644 --- a/wai/wai-quantum-qpu/src/ledger.rs +++ b/wai/wai-quantum-qpu/src/ledger.rs @@ -24,9 +24,11 @@ //! submission could be a second bill. They say so, and the person looks at the //! provider's own list. //! -//! A record holds the request, the program and the provider's id. It never -//! holds a credential. +//! A record holds the request, the program, the provider's id and, where a +//! client sends a program under the device's own names for its qubits, those +//! names as they were. It never holds a credential. +use crate::QubitNames; use crate::open_provider; use crate::registry::{config_dir, write_private}; use serde_json::{Value, json}; @@ -131,8 +133,8 @@ pub struct Ticket { impl Ticket { /// A job not yet sent: `sealing` is what its receipt will say of it. - pub(crate) fn recording(provider: &str, request: &Value, program: &NativeProgram, shots: u64, seed: Option, sealing: Value) -> Ticket { - Ticket { + pub(crate) fn recording(provider: &str, request: &Value, program: &NativeProgram, shots: u64, seed: Option, sealing: Value, names: Option<&QubitNames>) -> Ticket { + let mut ticket = Ticket { value: json!({ "format": FORMAT, "handle": new_handle(), @@ -146,7 +148,11 @@ impl Ticket { "sealing": sealing, "polls": 0, }), + }; + if let Some(names) = names { + ticket.value["qubit_names"] = json!(names); } + ticket } fn read(text: &str) -> Result { @@ -211,6 +217,15 @@ impl Ticket { open_provider::program_from_record(&self.value["program"]) } + /// The device's own names for the program's qubits, as they were when it + /// was sent, where its client sends it under them. + pub(crate) fn qubit_names(&self) -> Result, String> { + match self.value.get("qubit_names") { + None => Ok(None), + Some(names) => serde_json::from_value(names.clone()).map(Some).map_err(|e| format!("the record's qubit names: {e}")), + } + } + pub(crate) fn submitted(&mut self, id: &str) { self.value["state"] = json!("submitted"); self.value["id"] = json!(id); diff --git a/wai/wai-quantum-qpu/src/lib.rs b/wai/wai-quantum-qpu/src/lib.rs index d3b415545..90bece0ef 100644 --- a/wai/wai-quantum-qpu/src/lib.rs +++ b/wai/wai-quantum-qpu/src/lib.rs @@ -51,6 +51,7 @@ //! submit one that is not free unless the person confirms it, and seals the //! counts in a receipt with the engine ([`wai_quantum::engine::provider`]). +pub mod cloud_tasks; pub mod conformance; pub mod job; pub mod ledger; @@ -68,6 +69,7 @@ use wai_quantum::quantum::{Circuit, Gateset, circuit_hash}; use wai_quantum::quantum_fabric::sampling::{Counts, QpuCorpus, SampleFuture, Sampler, certify_sampler, check_counts, sample_kernel}; use wai_quantum::quantum_fabric::{FabricClass, KernelEntry, Member, MemberError, Presence, Provenance, RunMeter, ready}; use wai_quantum::quantum_toolchain::native::{self, Basis, Coupling, NativeError, NativeProgram}; +pub use wai_quantum::engine::provider::CountsSource; /// A key or token: kept out of `Debug` output. #[derive(Clone, PartialEq, Eq)] @@ -91,9 +93,26 @@ impl Secret { } /// `text` with every occurrence of the secret replaced by `…`: for a - /// message that might quote it back, such as a service's error body. + /// message that might quote it back, such as a service's error body. A + /// service's body is cut at a length, which can leave only the start of a + /// value it quotes, so a run of the secret's first 16 characters or more + /// goes too. pub fn redact(&self, text: &str) -> String { - if self.0.len() < 4 { text.to_string() } else { text.replace(&self.0, "…") } + if self.0.len() < 4 { + return text.to_string(); + } + let text = text.replace(&self.0, "…"); + let Some(head) = self.0.get(..16) else { return text }; + let (mut out, mut rest) = (String::with_capacity(text.len()), text.as_str()); + while let Some(at) = rest.find(head) { + out.push_str(&rest[..at]); + out.push('…'); + let after = &rest[at..]; + let same: usize = after.chars().zip(self.0.chars()).take_while(|(a, b)| a == b).map(|(a, _)| a.len_utf8()).sum(); + rest = &after[same..]; + } + out.push_str(rest); + out } } @@ -238,7 +257,7 @@ pub enum Billing { #[derive(Clone, Debug, PartialEq)] pub struct Details { /// The protocol the client speaks: `"wai.quantum.provider/1"`, or a - /// built-in client's (`"sampler-v2"`, `"prx-circuits"`, `"native-turns"`). + /// built-in client's (`"sampler-v2"`, `"prx-circuits"`, `"native-turns"`, `"cloud-tasks"`). pub protocol: String, /// The calibration snapshot, as the provider names it. pub calibration: Option, @@ -287,6 +306,57 @@ pub struct Estimate { pub seconds: Option, /// Anything else the provider says about it. pub note: Option, + /// The cost is the least it can come to, not what it will: what the job + /// takes decides the rest. + pub at_least: bool, +} + +/// What a client says of one job's counts, beside them, for its receipt +/// (wai/extensions/quantum-providers.md §8.3): where they come from, and, for +/// a provider that states it in its answer, how its device ran the program. +#[derive(Clone, Debug, Default, PartialEq)] +#[non_exhaustive] +pub struct Stated { + /// Shot records, the provider's own counts, or counts rebuilt from + /// probabilities; `None` when the client does not say, read as the + /// provider's own counts. + pub counts_source: Option, + /// `execution` as the provider states it (open protocol, §5.5); a built-in + /// client states its own before sending ([`Provider::execution`]). + pub execution: Option, +} + +impl Stated { + pub fn counts(source: CountsSource) -> Stated { + Stated { counts_source: Some(source), execution: None } + } + + pub fn with_execution(mut self, execution: Option) -> Stated { + self.execution = execution; + self + } +} + +/// A device's own name for each qubit index a program uses, when a client +/// sends it under names other than those indices: an index means what the +/// device's list said when the job was sent. +pub type QubitNames = std::collections::BTreeMap; + +/// Every qubit index `program` uses: its layouts and its gates. +pub(crate) fn program_qubits(program: &NativeProgram) -> std::collections::BTreeSet { + let mut used: std::collections::BTreeSet = program.layout.iter().chain(&program.final_layout).copied().collect(); + for g in &program.gates { + match *g { + native::NativeGate::Rz(q, _) | native::NativeGate::Sx(q) | native::NativeGate::X(q) | native::NativeGate::Prx(q, _, _) | native::NativeGate::Gpi(q, _) | native::NativeGate::Gpi2(q, _) => { + used.insert(q); + } + native::NativeGate::Cz(a, b) | native::NativeGate::Zz(a, b, _) => { + used.insert(a); + used.insert(b); + } + } + } + used } /// One service's client. @@ -327,6 +397,35 @@ pub trait Provider { fn cancel(&self, _id: &str) -> Result<(), QpuError> { Err(QpuError::Unsupported("this client cannot cancel a job".into())) } + + /// What this client sets at the provider when it sends `program`, as + /// a result's `execution` states it (§8.3), built from the very request it + /// sends: its members, the settings among them, the five things a + /// provider may do to a program (each `off`, `on`, or `default` when the + /// request sets nothing for it), and whether the gates are to run as + /// written. `None` when the client cannot say, or the provider states it + /// with the counts. + fn execution(&self, _program: &NativeProgram) -> Option { + None + } + + /// The device's own name for each qubit `program` uses, as the device is + /// now, when this client sends it under other names than the indices of + /// the device's coupling map. That list can change while a job waits, so + /// a job's record keeps these beside its program. By default: the indices + /// are the names. + fn qubit_names(&self, _program: &NativeProgram) -> Result, QpuError> { + Ok(None) + } + + /// [`Provider::counts`], with what the client says of them ([`Stated`]), + /// for a job sent earlier under `names` when its record holds them + /// ([`Provider::qubit_names`]): those are what its indices meant, whatever + /// the device has become. By default, the counts and nothing said. + fn counts_stated(&self, id: &str, program: &NativeProgram, shots: u64, _names: Option<&QubitNames>) -> Result<(Counts, JobInfo, Stated), QpuError> { + let (counts, info) = self.counts(id, program, shots)?; + Ok((counts, info, Stated::default())) + } } macro_rules! forward_provider { @@ -362,6 +461,18 @@ macro_rules! forward_provider { fn cancel(&self, id: &str) -> Result<(), QpuError> { (**self).cancel(id) } + + fn execution(&self, program: &NativeProgram) -> Option { + (**self).execution(program) + } + + fn qubit_names(&self, program: &NativeProgram) -> Result, QpuError> { + (**self).qubit_names(program) + } + + fn counts_stated(&self, id: &str, program: &NativeProgram, shots: u64, names: Option<&QubitNames>) -> Result<(Counts, JobInfo, Stated), QpuError> { + (**self).counts_stated(id, program, shots, names) + } }; } @@ -502,6 +613,12 @@ impl QpuSampler

{ /// `c`. A job that does not finish within the sampler's wait is /// cancelled, where the provider can cancel one. pub fn run_compiled(&self, c: &Circuit, program: &NativeProgram, shots: u64, seed: Option) -> Result<(Counts, JobRecord), QpuError> { + self.run_compiled_stated(c, program, shots, seed).map(|(counts, record, _)| (counts, record)) + } + + /// [`QpuSampler::run_compiled`], with what the client says of the counts + /// ([`Provider::counts_stated`]). + pub fn run_compiled_stated(&self, c: &Circuit, program: &NativeProgram, shots: u64, seed: Option) -> Result<(Counts, JobRecord, Stated), QpuError> { let id = self.provider.submit_seeded(program, shots, seed)?; let mut polls = 0u32; loop { @@ -518,7 +635,7 @@ impl QpuSampler

{ } } } - let (counts, info) = self.provider.counts(&id, program, shots)?; + let (counts, info, stated) = self.provider.counts_stated(&id, program, shots, None)?; let total: u64 = counts.iter().sum(); if counts.len() != 1usize << c.n_qubits || total != shots { return Err(QpuError::Counts(format!("{} outcomes summing to {total}, for {} qubits and {shots} shots", counts.len(), c.n_qubits))); @@ -535,7 +652,7 @@ impl QpuSampler

{ charged: info.charged, }; self.jobs.borrow_mut().push(record.clone()); - Ok((counts, record)) + Ok((counts, record, stated)) } /// A fabric [`Member`] of class [`FabricClass::Qpu`]: one sampling entry diff --git a/wai/wai-quantum-qpu/src/native_turns.rs b/wai/wai-quantum-qpu/src/native_turns.rs index 4d6882879..3b56b627f 100644 --- a/wai/wai-quantum-qpu/src/native_turns.rs +++ b/wai/wai-quantum-qpu/src/native_turns.rs @@ -18,7 +18,8 @@ //! the service's figure ([`JobInfo::service_kwh`]). use crate::transport::{Request, Transport}; -use crate::{Device, JobInfo, JobStatus, Provider, QpuError, Secret, counts_from_probabilities, field, field_str, json_ok, logical_index}; +use crate::{CountsSource, Device, JobInfo, JobStatus, Provider, QpuError, Secret, Stated, counts_from_probabilities, field, field_str, json_ok, logical_index}; +use wai_quantum::engine::provider::client_execution; use serde_json::{Value, json}; use wai_quantum::quantum_fabric::Provenance; use wai_quantum::quantum_fabric::sampling::Counts; @@ -74,8 +75,50 @@ impl NativeTurns { fn is_simulator(&self) -> bool { self.backend == "simulator" } + + /// The job's body, as [`Provider::submit`] sends it. + fn body(&self, program: &NativeProgram, shots: u64) -> Result { + if program.basis != Basis::GpiZz { + return Err(QpuError::Config("this service runs gpi, gpi2 and zz".into())); + } + let mut circuit: Vec = Vec::with_capacity(program.gates.len()); + let mut width = program.final_layout.iter().copied().max().map_or(0, |q| q + 1); + for g in &program.gates { + circuit.push(match *g { + NativeGate::Gpi(q, p) => { + width = width.max(q + 1); + json!({"gate": "gpi", "target": q, "phase": turns(p)}) + } + NativeGate::Gpi2(q, p) => { + width = width.max(q + 1); + json!({"gate": "gpi2", "target": q, "phase": turns(p)}) + } + NativeGate::Zz(a, b, t) => { + width = width.max(a.max(b) + 1); + json!({"gate": "zz", "targets": [a, b], "angle": turns(t)}) + } + other => return Err(QpuError::Config(format!("{other:?} is not one of this service's native gates"))), + }); + } + let mut body = json!({ + "type": JOB_TYPE, + "backend": self.backend, + "shots": shots, + "settings": {"error_mitigation": {"debiasing": false}}, + "input": {"gateset": "native", "qubits": width, "circuit": circuit} + }); + if self.is_simulator() + && let Some(model) = &self.noise + { + body["noise"] = json!({"model": model}); + } + Ok(body) + } } +/// The members of the body that set how the backend runs the program. +const SETTINGS: [&str; 3] = ["/input/gateset", "/settings/error_mitigation/debiasing", "/noise/model"]; + /// A dyadic angle in turns, `m/2^k`: exact in binary64. pub fn turns(d: Dyadic) -> f64 { let (m, k) = d.turns(); @@ -110,40 +153,7 @@ impl Provider for NativeTurns { } fn submit(&self, program: &NativeProgram, shots: u64) -> Result { - if program.basis != Basis::GpiZz { - return Err(QpuError::Config("this service runs gpi, gpi2 and zz".into())); - } - let mut circuit: Vec = Vec::with_capacity(program.gates.len()); - let mut width = program.final_layout.iter().copied().max().map_or(0, |q| q + 1); - for g in &program.gates { - circuit.push(match *g { - NativeGate::Gpi(q, p) => { - width = width.max(q + 1); - json!({"gate": "gpi", "target": q, "phase": turns(p)}) - } - NativeGate::Gpi2(q, p) => { - width = width.max(q + 1); - json!({"gate": "gpi2", "target": q, "phase": turns(p)}) - } - NativeGate::Zz(a, b, t) => { - width = width.max(a.max(b) + 1); - json!({"gate": "zz", "targets": [a, b], "angle": turns(t)}) - } - other => return Err(QpuError::Config(format!("{other:?} is not one of this service's native gates"))), - }); - } - let mut body = json!({ - "type": JOB_TYPE, - "backend": self.backend, - "shots": shots, - "settings": {"error_mitigation": {"debiasing": false}}, - "input": {"gateset": "native", "qubits": width, "circuit": circuit} - }); - if self.is_simulator() - && let Some(model) = &self.noise - { - body["noise"] = json!({"model": model}); - } + let body = self.body(program, shots)?; let r = self.transport.send(&self.authed(Request::post(format!("{}/jobs", self.base), body.to_string().into_bytes()).header("Content-Type", "application/json")))?; Ok(field_str(&json_ok(&r)?, &["id"])?.to_string()) } @@ -158,7 +168,18 @@ impl Provider for NativeTurns { }) } + fn execution(&self, program: &NativeProgram) -> Option { + let body = self.body(program, 1).ok()?; + Some(client_execution(&body, &SETTINGS, &[], ["off", "default", "default", "default", "default"], false, "debiasing is sent off and the program goes in the service's native gate set; the request sets nothing for twirling, decoupling, remapping or optimisation, so the service's defaults stand")) + } + fn counts(&self, id: &str, program: &NativeProgram, shots: u64) -> Result<(Counts, JobInfo), QpuError> { + self.counts_stated(id, program, shots, None).map(|(c, i, _)| (c, i)) + } + + /// The service answers probabilities, so the counts are always rebuilt + /// from them here: derived, not shot records. + fn counts_stated(&self, id: &str, program: &NativeProgram, shots: u64, _names: Option<&crate::QubitNames>) -> Result<(Counts, JobInfo, Stated), QpuError> { let job = json_ok(&self.transport.send(&self.authed(Request::get(format!("{}/jobs/{id}", self.base))))?)?; let service_kwh = job.get("stats").and_then(|s| s.get("kwh")).and_then(|k| k.as_f64()); let r = self.transport.send(&self.authed(Request::get(format!("{}/jobs/{id}/results/probabilities", self.base))))?; @@ -170,7 +191,7 @@ impl Provider for NativeTurns { let prob = prob.as_f64().filter(|x| x.is_finite() && *x >= 0.0).ok_or_else(|| QpuError::Counts(format!("probability of {key:?}")))?; p[logical_index(physical, &program.final_layout)] += prob; } - Ok((counts_from_probabilities(&p, shots), JobInfo { service_kwh, ..JobInfo::default() })) + Ok((counts_from_probabilities(&p, shots), JobInfo { service_kwh, ..JobInfo::default() }, Stated::counts(CountsSource::Probabilities))) } } diff --git a/wai/wai-quantum-qpu/src/open_provider.rs b/wai/wai-quantum-qpu/src/open_provider.rs index 2098fa1b2..8388a3060 100644 --- a/wai/wai-quantum-qpu/src/open_provider.rs +++ b/wai/wai-quantum-qpu/src/open_provider.rs @@ -25,7 +25,7 @@ //! every other. use crate::transport::{Request, Transport}; -use crate::{Billing, Cost, Details, Device, Estimate, JobInfo, JobStatus, Provider, QpuError, Secret, basis_label, json_ok}; +use crate::{Billing, Cost, CountsSource, Details, Device, Estimate, JobInfo, JobStatus, Provider, QpuError, Secret, Stated, basis_label, json_ok}; use serde_json::{Map, Value, json}; use std::cell::RefCell; use std::io::{BufRead, BufReader, Write}; @@ -420,6 +420,7 @@ pub fn estimate_of(a: &Value) -> Result { unit: a.get("unit").and_then(Value::as_str).map(String::from), seconds: a.get("seconds").and_then(Value::as_f64).filter(|s| s.is_finite() && *s >= 0.0), note: a.get("note").and_then(Value::as_str).map(|n| n.chars().take(400).collect()), + at_least: false, }) } @@ -677,7 +678,15 @@ impl Provider for OpenProvider { }) } - fn counts(&self, id: &str, program: &NativeProgram, _shots: u64) -> Result<(Counts, JobInfo), QpuError> { + fn counts(&self, id: &str, program: &NativeProgram, shots: u64) -> Result<(Counts, JobInfo), QpuError> { + self.counts_stated(id, program, shots, None).map(|(c, i, _)| (c, i)) + } + + /// The `result` answer's counts, and what it states of them (§5.5): where + /// they come from (the provider's own counts when it does not say) and how + /// its device ran the program (`None` when it does not say). A statement + /// that is not of its form is refused, not sealed. + fn counts_stated(&self, id: &str, program: &NativeProgram, _shots: u64, _names: Option<&crate::QubitNames>) -> Result<(Counts, JobInfo, Stated), QpuError> { let a = self.call("result", json!({ "job": id }))?; let counts = counts_of(&a, program.n_qubits)?; let calibration = a.get("calibration").and_then(Value::as_str).map(String::from).or_else(|| self.describe().ok().and_then(|d| d.pointer("/calibration/id").and_then(Value::as_str).map(String::from))); @@ -686,7 +695,15 @@ impl Provider for OpenProvider { calibration, charged: a.get("charged").and_then(cost_of), }; - Ok((counts, info)) + let source = match a.get("counts_source") { + None | Some(Value::Null) => CountsSource::VendorCounts, + Some(v) => v.as_str().and_then(CountsSource::from_label).ok_or_else(|| QpuError::Protocol("result: `counts_source` is shots, vendor_counts or probabilities".into()))?, + }; + let execution = match a.get("execution") { + None | Some(Value::Null) => None, + Some(e) => Some(wai_quantum::engine::provider::provider_execution(e).map_err(|why| QpuError::Protocol(format!("result: {why}")))?), + }; + Ok((counts, info, Stated::counts(source).with_execution(execution))) } fn cancel(&self, id: &str) -> Result<(), QpuError> { diff --git a/wai/wai-quantum-qpu/src/prx_circuits.rs b/wai/wai-quantum-qpu/src/prx_circuits.rs index c266de287..563f287f7 100644 --- a/wai/wai-quantum-qpu/src/prx_circuits.rs +++ b/wai/wai-quantum-qpu/src/prx_circuits.rs @@ -18,7 +18,8 @@ //! binary64 to `π·m/2^(k−1)`, not exactly. use crate::transport::{Request, Transport}; -use crate::{Device, JobInfo, JobStatus, Provider, QpuError, Secret, field, field_str, json_ok}; +use crate::{CountsSource, Device, JobInfo, JobStatus, Provider, QpuError, Secret, Stated, field, field_str, json_ok}; +use wai_quantum::engine::provider::client_execution; use serde_json::{Value, json}; use std::cell::RefCell; use wai_quantum::quantum_fabric::Provenance; @@ -63,6 +64,26 @@ impl PrxCircuits { fn name(&self, q: u16) -> Result { self.names.borrow().get(usize::from(q)).cloned().ok_or_else(|| QpuError::Config(format!("physical qubit {q} has no name; read the device first"))) } + + /// The job's body, as [`Provider::submit`] sends it: `circuits` and + /// `shots`, and nothing else. + fn body(&self, program: &NativeProgram, shots: u64) -> Result { + if program.basis != Basis::PrxCz { + return Err(QpuError::Config("this service runs prx and cz".into())); + } + let mut instructions: Vec = Vec::with_capacity(program.gates.len() + program.final_layout.len()); + for g in &program.gates { + instructions.push(match *g { + NativeGate::Prx(q, theta, phi) => json!({"name": "prx", "locus": [self.name(q)?], "args": {"angle": radians(theta), "phase": radians(phi)}}), + NativeGate::Cz(a, b) => json!({"name": "cz", "locus": [self.name(a)?, self.name(b)?], "args": {}}), + other => return Err(QpuError::Config(format!("{other:?} is not one of this service's instructions"))), + }); + } + for (i, &p) in program.final_layout.iter().enumerate() { + instructions.push(json!({"name": "measure", "locus": [self.name(p)?], "args": {"key": format!("c{i}")}})); + } + Ok(json!({"circuits": [{"name": "wai", "instructions": instructions}], "shots": shots})) + } } /// A dyadic angle in radians, in `(−π, π]`. @@ -117,21 +138,7 @@ impl Provider for PrxCircuits { } fn submit(&self, program: &NativeProgram, shots: u64) -> Result { - if program.basis != Basis::PrxCz { - return Err(QpuError::Config("this service runs prx and cz".into())); - } - let mut instructions: Vec = Vec::with_capacity(program.gates.len() + program.final_layout.len()); - for g in &program.gates { - instructions.push(match *g { - NativeGate::Prx(q, theta, phi) => json!({"name": "prx", "locus": [self.name(q)?], "args": {"angle": radians(theta), "phase": radians(phi)}}), - NativeGate::Cz(a, b) => json!({"name": "cz", "locus": [self.name(a)?, self.name(b)?], "args": {}}), - other => return Err(QpuError::Config(format!("{other:?} is not one of this service's instructions"))), - }); - } - for (i, &p) in program.final_layout.iter().enumerate() { - instructions.push(json!({"name": "measure", "locus": [self.name(p)?], "args": {"key": format!("c{i}")}})); - } - let body = json!({"circuits": [{"name": "wai", "instructions": instructions}], "shots": shots}); + let body = self.body(program, shots)?; let r = self.transport.send(&self.authed(Request::post(format!("{}/jobs/{}/circuit", self.base, self.qc), body.to_string().into_bytes()).header("Content-Type", "application/json; charset=UTF-8")))?; Ok(field_str(&json_ok(&r)?, &["id"])?.to_string()) } @@ -147,7 +154,21 @@ impl Provider for PrxCircuits { }) } - fn counts(&self, id: &str, program: &NativeProgram, _shots: u64) -> Result<(Counts, JobInfo), QpuError> { + fn execution(&self, program: &NativeProgram) -> Option { + let body = self.body(program, 1).ok()?; + Some(client_execution(&body, &[], &[], ["default"; 5], false, "this client sends only circuits and shots: it sets nothing, so every step is the service's default, whatever that is")) + } + + fn counts(&self, id: &str, program: &NativeProgram, shots: u64) -> Result<(Counts, JobInfo), QpuError> { + self.counts_stated(id, program, shots, None).map(|(c, i, _)| (c, i)) + } + + /// The service's own counts, keyed by bit strings. + fn qubit_names(&self, program: &NativeProgram) -> Result, QpuError> { + crate::program_qubits(program).into_iter().map(|q| Ok((q, self.name(q)?))).collect::>().map(Some) + } + + fn counts_stated(&self, id: &str, program: &NativeProgram, _shots: u64, _names: Option<&crate::QubitNames>) -> Result<(Counts, JobInfo, Stated), QpuError> { let r = self.transport.send(&self.authed(Request::get(format!("{}/jobs/{id}/artifacts/measurement_counts", self.base))).header("Accept-Encoding", "identity"))?; let v = json_ok(&r)?; let first = v.get(0).ok_or_else(|| QpuError::Protocol("no circuit's counts".into()))?; @@ -174,7 +195,7 @@ impl Provider for PrxCircuits { let x = (0..n).fold(0usize, |x, i| x | (usize::from(chars[position[i]] - b'0') << i)); counts[x] += k.as_u64().ok_or_else(|| QpuError::Counts(format!("count of {bits:?}")))?; } - Ok((counts, JobInfo::default())) + Ok((counts, JobInfo::default(), Stated::counts(CountsSource::VendorCounts))) } } diff --git a/wai/wai-quantum-qpu/src/reference.rs b/wai/wai-quantum-qpu/src/reference.rs index 1db64aee5..404f1a280 100644 --- a/wai/wai-quantum-qpu/src/reference.rs +++ b/wai/wai-quantum-qpu/src/reference.rs @@ -378,7 +378,21 @@ impl Reference { if job.polls_left > 0 { return error("refused", format!("job {id} has not finished")); } - let mut a = json!({ "ok": true, "counts": job.counts, "shots": job.shots, "calibration": self.calibration_id() }); + // What a provider states of its counts and of how it ran the + // program (§5.5): shots drawn one by one, and the gates + // simulated as written, with nothing a device would add. + let mut a = json!({ + "ok": true, + "counts": job.counts, + "shots": job.shots, + "calibration": self.calibration_id(), + "counts_source": "shots", + "execution": { + "mitigation": "none", "twirling": "none", "decoupling": "none", "remapping": "none", "optimisation": "none", + "verbatim": true, + "note": "the reference simulates the program's gates as written, on the qubits named, and corrects nothing" + }, + }); if let (Some(amount), Some((_, currency, _))) = (&job.charged, &self.config.price) { a["charged"] = json!({ "amount": amount, "currency": currency }); } diff --git a/wai/wai-quantum-qpu/src/registry.rs b/wai/wai-quantum-qpu/src/registry.rs index b2828e15f..e08c850f4 100644 --- a/wai/wai-quantum-qpu/src/registry.rs +++ b/wai/wai-quantum-qpu/src/registry.rs @@ -3,7 +3,7 @@ //! //! The list is `quantum-providers.json` in `$XDG_CONFIG_HOME/wai/` (or //! `~/.config/wai/`, or `%APPDATA%\wai\`), or the file `$WAI_QUANTUM_PROVIDERS` -//! names. Each entry names a provider, its kind (one of the three built-in +//! names. Each entry names a provider, its kind (one of the four built-in //! protocols, `http` or `process`), its endpoint or command, its non-secret //! settings, and where its credential is: nowhere, the keychain, a file, or an //! environment variable it names. The list holds no credential. @@ -22,6 +22,7 @@ //! crate's README) when no credential is stored for them. use crate::job::Target; +use crate::cloud_tasks::{CloudTasks, Keys}; use crate::native_turns::NativeTurns; use crate::open_provider::{Channel, Http, OpenProvider, Process}; use crate::prx_circuits::PrxCircuits; @@ -40,12 +41,17 @@ pub const FILE_NAME: &str = "quantum-providers.json"; /// The service name a credential is kept under in the keychain. pub const SERVICE: &str = "wai-quantum-provider"; -/// What kind of provider an entry is. +/// What kind of provider an entry is. A minor release may add a kind, so a +/// `match` outside this crate takes a wildcard arm; inside it a `match` stays +/// exhaustive, and a kind added without its name, its settings and its +/// client does not build. #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] pub enum Kind { SamplerV2, PrxCircuits, NativeTurns, + CloudTasks, /// The open protocol over HTTP. Http, /// The open protocol to a local program. @@ -53,20 +59,23 @@ pub enum Kind { } impl Kind { - pub const ALL: [Kind; 5] = [Kind::SamplerV2, Kind::PrxCircuits, Kind::NativeTurns, Kind::Http, Kind::Process]; + /// Every kind, the built-in protocols first. A slice, so that a kind + /// added later changes no type. + pub const ALL: &'static [Kind] = &[Kind::SamplerV2, Kind::PrxCircuits, Kind::NativeTurns, Kind::CloudTasks, Kind::Http, Kind::Process]; pub fn name(self) -> &'static str { match self { Kind::SamplerV2 => "sampler-v2", Kind::PrxCircuits => "prx-circuits", Kind::NativeTurns => "native-turns", + Kind::CloudTasks => "cloud-tasks", Kind::Http => "http", Kind::Process => "process", } } pub fn parse(s: &str) -> Option { - Kind::ALL.into_iter().find(|k| k.name() == s) + Kind::ALL.iter().copied().find(|k| k.name() == s) } /// The protocol the kind speaks. @@ -84,6 +93,7 @@ impl Kind { Kind::SamplerV2 => Some(&["crn", "backend", "iam"]), Kind::PrxCircuits => Some(&["qc"]), Kind::NativeTurns => Some(&["backend", "noise"]), + Kind::CloudTasks => Some(&["region", "device", "bucket", "prefix", "basis"]), Kind::Http | Kind::Process => None, } } @@ -117,7 +127,8 @@ impl Credential { pub struct Entry { pub name: String, pub kind: Kind, - /// `http`: the URL; a built-in kind: another endpoint than its own. + /// `http`: the URL; a built-in kind but `cloud-tasks`: another endpoint + /// than its own. pub endpoint: Option, /// `process`: the program and its arguments. pub command: Vec, @@ -156,6 +167,18 @@ impl Entry { return Err("a process provider has a command: the program and its arguments".into()); } } + Kind::CloudTasks => { + if self.endpoint.is_some() { + return Err("cloud-tasks takes no --endpoint: it reaches the service's own hosts for the region and the bucket".into()); + } + let named = |key: &str| self.settings.get(key).map(|v| v.as_str().map(str::to_string).ok_or(format!("the setting {key} is a name"))); + if let Some(region) = named("region") { + crate::cloud_tasks::check_region(®ion?).map_err(|e| format!("the setting region: {e}"))?; + } + if let Some(bucket) = named("bucket") { + crate::cloud_tasks::check_bucket(&bucket?).map_err(|e| format!("the setting bucket: {e}"))?; + } + } _ => { if let Some(e) = &self.endpoint && !e.starts_with("https://") @@ -198,7 +221,8 @@ impl Entry { fn from_json(v: &Value) -> Result { let name = v.get("name").and_then(Value::as_str).ok_or("an entry has a name")?.to_string(); - let kind = v.get("kind").and_then(Value::as_str).and_then(Kind::parse).ok_or_else(|| format!("{name}: its kind is one of sampler-v2, prx-circuits, native-turns, http, process"))?; + let kinds: Vec<&str> = Kind::ALL.iter().map(|k| k.name()).collect(); + let kind = v.get("kind").and_then(Value::as_str).and_then(Kind::parse).ok_or_else(|| format!("{name}: its kind is one of {}", kinds.join(", ")))?; let credential = match v.get("credential") { None | Some(Value::Null) => Credential::None, Some(c) if c == "keychain" => Credential::Keychain, @@ -267,6 +291,10 @@ pub(crate) fn write_private(path: &Path, text: &[u8]) -> std::io::Result<()> { pub struct Registry { path: PathBuf, entries: Vec, + /// Entries of a kind this build does not know, written by a later one: kept + /// as they are, and written back with the list. + kept: Vec, + notes: Vec, } impl Registry { @@ -281,22 +309,36 @@ impl Registry { let path = path.into(); let text = match std::fs::read_to_string(&path) { Ok(t) => t, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Registry { path, entries: Vec::new() }), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Registry { path, entries: Vec::new(), kept: Vec::new(), notes: Vec::new() }), Err(e) => return Err(format!("{}: {e}", path.display())), }; let v: Value = serde_json::from_str(&text).map_err(|e| format!("{}: not JSON: {e}", path.display()))?; if v.get("version").and_then(Value::as_u64) != Some(1) { return Err(format!("{}: a providers list of version 1", path.display())); } - let mut entries: Vec = Vec::new(); - for e in v.get("providers").and_then(Value::as_array).into_iter().flatten() { - let e = Entry::from_json(e).map_err(|m| format!("{}: {m}", path.display()))?; - if entries.iter().any(|x| x.name == e.name) { - return Err(format!("{}: {} is listed twice", path.display(), e.name)); + let (mut entries, mut kept, mut notes): (Vec, Vec, Vec) = (Vec::new(), Vec::new(), Vec::new()); + let mut names: Vec = Vec::new(); + for raw in v.get("providers").and_then(Value::as_array).into_iter().flatten() { + let name = raw.get("name").and_then(Value::as_str).ok_or_else(|| format!("{}: an entry has a name", path.display()))?.to_string(); + if names.contains(&name) { + return Err(format!("{}: {name} is listed twice", path.display())); } - entries.push(e); + names.push(name.clone()); + // A kind a later build added: this one leaves it as it is, and uses the rest. + if let Some(kind) = raw.get("kind").and_then(Value::as_str).filter(|k| Kind::parse(k).is_none()) { + notes.push(format!("{name}: its kind {kind:?} is not one this build knows; it is left as it is, and not used here")); + kept.push(raw.clone()); + continue; + } + entries.push(Entry::from_json(raw).map_err(|m| format!("{}: {m}", path.display()))?); } - Ok(Registry { path, entries }) + Ok(Registry { path, entries, kept, notes }) + } + + /// What loading the list found to say: each entry of a kind this build does + /// not know, which it keeps and does not use. + pub fn notes(&self) -> &[String] { + &self.notes } /// The list at [`Registry::default_path`]. @@ -319,6 +361,9 @@ impl Registry { /// Adds `entry`, replacing one of the same name only when `replace`. pub fn add(&mut self, entry: Entry, replace: bool) -> Result<(), String> { entry.check()?; + if self.kept.iter().any(|k| k.get("name").and_then(Value::as_str) == Some(entry.name.as_str())) { + return Err(format!("{} is listed already, by a later build, as a kind this one does not know: it is left as it is", entry.name)); + } match self.entries.iter().position(|e| e.name == entry.name) { Some(_) if !replace => Err(format!("{} is already listed; --replace replaces it", entry.name)), Some(i) => { @@ -340,7 +385,8 @@ impl Registry { /// Writes the list back, readable by its owner only. pub fn save(&self) -> Result<(), String> { - let v = json!({ "version": 1, "providers": self.entries.iter().map(Entry::to_json).collect::>() }); + let providers: Vec = self.entries.iter().map(Entry::to_json).chain(self.kept.iter().cloned()).collect(); + let v = json!({ "version": 1, "providers": providers }); let text = serde_json::to_string_pretty(&v).map_err(|e| e.to_string())? + "\n"; write_private(&self.path, text.as_bytes()).map_err(|e| format!("{}: {e}", self.path.display())) } @@ -667,8 +713,15 @@ pub fn channel(entry: &Entry, secret: Option) -> Result /// `stores` (or, for a built-in kind, its environment variables). pub fn open(entry: &Entry, stores: &Stores) -> Result { let secret = stores.get(entry)?; + // A built-in kind falls back to its environment only when the entry names no + // credential: one that names a place and finds nothing there must not sign + // with whatever else happens to be set. + if secret.is_none() && entry.credential != Credential::None && !matches!(entry.kind, Kind::Http | Kind::Process) { + return Err(format!("{}'s credential, kept in {}, gives nothing: keep it again, or set what it names", entry.name, entry.credential.describe())); + } let env_secret = |var: &str| secret.clone().or_else(|| Secret::from_env(var).ok()); let curl = || Curl::new(120); + let mut parts = Vec::new(); let (provider, secret): (Box, Option) = match entry.kind { Kind::SamplerV2 => { let key = need(env_secret("WAI_QPU_SAMPLER_API_KEY"), "sampler-v2 needs its API key: --credential-stdin, or WAI_QPU_SAMPLER_API_KEY")?; @@ -699,9 +752,27 @@ pub fn open(entry: &Entry, stores: &Stores) -> Result { } (Box::new(p), Some(key)) } + Kind::CloudTasks => { + let keys = match &secret { + Some(s) => Keys::parse(s).map_err(|e| e.to_string())?, + None => Keys::from_env().map_err(|e| e.to_string())?, + }; + let region = need(setting(entry, "region", "WAI_QPU_TASKS_REGION"), "cloud-tasks needs the setting region (or WAI_QPU_TASKS_REGION)")?; + let device = need(setting(entry, "device", "WAI_QPU_TASKS_DEVICE"), "cloud-tasks needs the setting device, the device's ARN (or WAI_QPU_TASKS_DEVICE)")?; + let bucket = need(setting(entry, "bucket", "WAI_QPU_TASKS_BUCKET"), "cloud-tasks needs the setting bucket, your own results bucket (or WAI_QPU_TASKS_BUCKET)")?; + parts = keys.parts(); + let mut p = CloudTasks::new(curl(), keys, region, device, bucket).map_err(|e| e.to_string())?; + if let Some(prefix) = setting(entry, "prefix", "WAI_QPU_TASKS_PREFIX") { + p = p.with_prefix(prefix); + } + if let Some(basis) = setting(entry, "basis", "WAI_QPU_TASKS_BASIS") { + p = p.with_basis(crate::open_provider::basis_named(&basis).ok_or_else(|| format!("no basis {basis:?}: rzsxxcz, prxcz or gpizz"))?); + } + (Box::new(p), secret.clone()) + } Kind::Http | Kind::Process => (Box::new(OpenProvider::new(channel(entry, secret.clone())?).with_settings(entry.settings.clone())), secret), }; - Ok(Target::new(entry.name.clone(), entry.kind, provider, secret)) + Ok(Target::new(entry.name.clone(), entry.kind, provider, secret).with_secrets(parts)) } #[cfg(test)] @@ -845,4 +916,104 @@ mod tests { let _ = Command::new("/usr/bin/security").args(["delete-keychain", file.to_str().unwrap()]).status(); let _ = std::fs::remove_dir_all(&dir); } + + fn tasks_entry() -> Entry { + let mut e = entry("tasks", Kind::CloudTasks); + for (k, v) in [("region", "us-east-1"), ("device", "arn:device"), ("bucket", "my-results")] { + e.settings.insert(k.into(), json!(v)); + } + e + } + + #[test] + fn a_named_credential_that_gives_nothing_is_an_error_saying_where_it_looked() { + for (credential, place) in [(Credential::Keychain, "keychain"), (Credential::File, "file"), (Credential::Env("WAI_QPU_TEST_NOTHING_HERE".into()), "$WAI_QPU_TEST_NOTHING_HERE")] { + for kind in [Kind::CloudTasks, Kind::SamplerV2, Kind::PrxCircuits, Kind::NativeTurns] { + let mut e = if kind == Kind::CloudTasks { tasks_entry() } else { entry("x", kind) }; + e.credential = credential.clone(); + let why = open(&e, &Stores::memory()).err().unwrap_or_else(|| panic!("{} opened with no credential", kind.name())); + assert!(why.contains(place), "{}: {why}", kind.name()); + assert!(why.contains("gives nothing"), "{}: {why}", kind.name()); + } + } + } + + #[test] + fn cloud_tasks_refuses_an_endpoint_it_would_not_use() { + assert!(tasks_entry().check().is_ok()); + let mut e = tasks_entry(); + e.endpoint = Some("http://127.0.0.1:4566".into()); + assert!(e.check().is_err_and(|why| why.contains("no --endpoint"))); + } + + #[test] + fn a_region_or_a_bucket_that_is_not_one_is_refused_before_anything_is_sent() { + for (key, bad) in [("region", "us-east-1.evil.example/"), ("region", "US-EAST-1"), ("region", ""), ("bucket", "evil.example/x"), ("bucket", "Upper"), ("bucket", "ab"), ("bucket", "a..b"), ("bucket", "a.-b"), ("bucket", "-ab"), ("bucket", "192.168.1.1")] { + let mut e = tasks_entry(); + e.settings.insert(key.into(), json!(bad)); + assert!(e.check().is_err_and(|why| why.contains(key)), "{key}={bad}"); + } + // What a person may name: a dotted bucket is legal, and is read by path. + for good in ["amazon-braket-a.b", "abc", "a-b.c-d.e"] { // allow-name: the prefix the service requires + let mut e = tasks_entry(); + e.settings.insert("bucket".into(), json!(good)); + assert!(e.check().is_ok(), "{good}"); + } + } + + #[test] + fn every_part_of_kept_keys_is_taken_out_of_a_message_whole_or_cut_short() { + let (id, secret, token) = ("AKIDEXAMPLE0123456789", "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", "IQoJb3JpZ2luX2VjEXAMPLETOKENIQoJb3JpZ2luX2VjEXAMPLETOKEN"); + let stores = Stores::memory(); + let (credential, _) = stores.keep("tasks", &Secret::new(format!("{id}:{secret}:{token}"))).unwrap(); + let mut e = tasks_entry(); + e.credential = credential; + let t = open(&e, &stores).unwrap_or_else(|why| panic!("{why}")); + for part in [id, secret, token] { + assert!(!t.redact(&format!("{part}")).contains(part), "{part}"); + } + // A service's answer is cut at a length: what is left of the token goes too. + let cut = t.redact(&format!("{}", &token[..30])); + assert!(!cut.contains(&token[..30]) && !cut.contains(&token[..16]), "{cut}"); + // What is not a credential stays. + assert_eq!(t.redact("the request expired"), "the request expired"); + } + + /// A list written by a later build, with a kind this one does not know: + /// the rest is read, and the entry is kept as it was when the list is saved. + #[test] + fn a_kind_this_build_does_not_know_is_kept_and_the_rest_is_read() { + let dir = temp("unknown-kind"); + let path = dir.join(FILE_NAME); + std::fs::create_dir_all(&dir).unwrap(); + let later = json!({ "name": "later", "kind": "a-kind-from-later", "endpoint": "https://example.invalid/q", "settings": { "depth": 3, "ratio": 0.25, "nested": { "b": [1, 2], "a": null } }, "credential": { "env": "LATER_TOKEN" } }); + let file = json!({ "version": 1, "providers": [ + { "name": "lab", "kind": "process", "command": ["wai-qpu", "reference", "--stdio"], "credential": null }, + later, + { "name": "tasks", "kind": "cloud-tasks", "settings": { "region": "us-east-1", "device": "arn:device", "bucket": "my-results" }, "credential": null }, + ] }); + std::fs::write(&path, serde_json::to_string_pretty(&file).unwrap()).unwrap(); + let mut r = Registry::load(&path).unwrap_or_else(|e| panic!("one entry this build does not know stopped every other: {e}")); + assert_eq!(r.entries().iter().map(|e| e.name.as_str()).collect::>(), ["lab", "tasks"]); + assert!(r.notes().len() == 1 && r.notes()[0].contains("later") && r.notes()[0].contains("a-kind-from-later"), "{:?}", r.notes()); + let mut extra = entry("more", Kind::Process); + extra.command = vec!["x".into()]; + r.add(extra, false).unwrap(); + r.save().unwrap(); + let saved = std::fs::read_to_string(&path).unwrap(); + let back: Value = serde_json::from_str(&saved).unwrap(); + let kept: Vec<&Value> = back["providers"].as_array().unwrap().iter().filter(|e| e["name"] == "later").collect(); + assert_eq!(kept, vec![&later], "the entry is kept, every member of it"); + let text = serde_json::to_string_pretty(&later).unwrap().replace('\n', "\n "); + assert!(saved.contains(&text), "the entry is written as it was:\n{saved}"); + // A name an entry holds already, known or not, is not listed twice. + let mut twin = entry("later", Kind::Process); + twin.command = vec!["x".into()]; + assert!(r.add(twin, false).is_err()); + // An entry with no kind at all is still an error, and says every kind. + std::fs::write(&path, r#"{"version": 1, "providers": [{"name": "x"}]}"#).unwrap(); + let why = Registry::load(&path).unwrap_err(); + assert!(Kind::ALL.iter().all(|k| why.contains(k.name())), "{why}"); + let _ = std::fs::remove_dir_all(&dir); + } } diff --git a/wai/wai-quantum-qpu/src/sampler_v2.rs b/wai/wai-quantum-qpu/src/sampler_v2.rs index eafca1bfc..d08fea4ed 100644 --- a/wai/wai-quantum-qpu/src/sampler_v2.rs +++ b/wai/wai-quantum-qpu/src/sampler_v2.rs @@ -15,7 +15,8 @@ //! hex string per shot, whose bit `i` is `c[i]`. use crate::transport::{Request, Transport}; -use crate::{Device, JobInfo, JobStatus, Provider, QpuError, Secret, field, field_str, json_ok}; +use crate::{CountsSource, Device, JobInfo, JobStatus, Provider, QpuError, Secret, Stated, field, field_str, json_ok}; +use wai_quantum::engine::provider::client_execution; use serde_json::json; use std::cell::RefCell; use wai_quantum::quantum_fabric::Provenance; @@ -92,6 +93,23 @@ impl SamplerV2 { Ok(token) } + /// The job's body, as [`Provider::submit`] sends it: one PUB of the + /// program on physical qubits, dynamical decoupling and twirling off. + fn body(&self, program: &NativeProgram, shots: u64) -> serde_json::Value { + json!({ + "program_id": "sampler", + "backend": self.backend, + "params": { + "version": 2, + "pubs": [[program.qasm3(), null, shots]], + "options": { + "dynamical_decoupling": {"enable": false}, + "twirling": {"enable_gates": false, "enable_measure": false} + } + } + }) + } + fn authed(&self, r: Request) -> Result { Ok(r.header("Authorization", format!("Bearer {}", self.bearer()?)).header("Service-CRN", self.crn.clone()).header(VERSION_HEADER, API_VERSION).header("Accept", "application/json")) } @@ -107,6 +125,9 @@ fn form_encode(s: &str) -> String { .collect() } +/// The members of the body that set how the backend runs the program. +const SETTINGS: [&str; 3] = ["/params/options/dynamical_decoupling/enable", "/params/options/twirling/enable_gates", "/params/options/twirling/enable_measure"]; + /// The backend's two-qubit pairs: the `cz` gate's own map, else the /// backend's. fn pairs(config: &serde_json::Value) -> Result, QpuError> { @@ -140,25 +161,16 @@ impl Provider for SamplerV2 { } } let coupling = Coupling::new(n as u16, &pairs(&v)?).map_err(QpuError::Compile)?; - Ok(Device { name: format!("sampler-v2:{}", self.backend), basis: Basis::RzSxXCz, coupling, provenance: Provenance::Silicon }) + // The service says when a backend is a simulator; its counts are not measured. + let provenance = if v.get("simulator").and_then(serde_json::Value::as_bool) == Some(true) { Provenance::Simulator(format!("the service's {} simulator", self.backend)) } else { Provenance::Silicon }; + Ok(Device { name: format!("sampler-v2:{}", self.backend), basis: Basis::RzSxXCz, coupling, provenance }) } fn submit(&self, program: &NativeProgram, shots: u64) -> Result { if program.basis != Basis::RzSxXCz { return Err(QpuError::Config("this service runs rz, sx, x and cz".into())); } - let body = json!({ - "program_id": "sampler", - "backend": self.backend, - "params": { - "version": 2, - "pubs": [[program.qasm3(), null, shots]], - "options": { - "dynamical_decoupling": {"enable": false}, - "twirling": {"enable_gates": false, "enable_measure": false} - } - } - }); + let body = self.body(program, shots); let r = self.transport.send(&self.authed(Request::post(format!("{}/jobs", self.base), body.to_string().into_bytes()).header("Content-Type", "application/json"))?)?; Ok(field_str(&json_ok(&r)?, &["id"])?.to_string()) } @@ -177,7 +189,17 @@ impl Provider for SamplerV2 { }) } - fn counts(&self, id: &str, program: &NativeProgram, _shots: u64) -> Result<(Counts, JobInfo), QpuError> { + fn execution(&self, program: &NativeProgram) -> Option { + // Read from the body itself, so that what is stated is what is sent. + Some(client_execution(&self.body(program, 1), &SETTINGS, &[], ["default", "off", "off", "default", "default"], false, "dynamical decoupling and twirling of gates and measurements are sent off; the request sets nothing for mitigation, remapping or optimisation, so the service's defaults stand; the program is sent on physical qubits in the backend's own gates")) + } + + fn counts(&self, id: &str, program: &NativeProgram, shots: u64) -> Result<(Counts, JobInfo), QpuError> { + self.counts_stated(id, program, shots, None).map(|(c, i, _)| (c, i)) + } + + /// One sample per shot: the counts are tallied here from shot records. + fn counts_stated(&self, id: &str, program: &NativeProgram, _shots: u64, _names: Option<&crate::QubitNames>) -> Result<(Counts, JobInfo, Stated), QpuError> { let r = self.transport.send(&self.authed(Request::get(format!("{}/jobs/{id}/results", self.base)))?)?; let v = json_ok(&r)?; let samples = field(&v, &["results"])? @@ -194,7 +216,7 @@ impl Provider for SamplerV2 { values.push((u64::from_str_radix(hex, 16).map_err(|_| QpuError::Protocol(format!("sample {s:?} is not hex")))?, 1u64)); } let counts = counts_from_integers(program.n_qubits, values).map_err(|e| QpuError::Counts(e.to_string()))?; - Ok((counts, JobInfo::default())) + Ok((counts, JobInfo::default(), Stated::counts(CountsSource::Shots))) } } @@ -217,4 +239,20 @@ mod tests { assert!(pairs(&json!({"coupling_map": [[0]]})).is_err()); assert!(pairs(&json!({})).is_err()); } + + /// A backend the service says is a simulator is sealed as one. + #[test] + fn a_simulated_backend_is_not_silicon() { + let config = |simulator: serde_json::Value| { + json!({ "n_qubits": 2, "basis_gates": ["rz", "sx", "x", "cz"], "coupling_map": [[0, 1], [1, 0]], "simulator": simulator }) + }; + let at = |simulator: serde_json::Value| { + let transport = crate::transport::Scripted::new(move |r: &Request| { + if r.url.contains("token") { crate::transport::Response::json(200, &json!({ "access_token": "t", "expires_in": 3600 })) } else { crate::transport::Response::json(200, &config(simulator.clone())) } + }); + SamplerV2::new(transport, Secret::new("k"), "crn:test", "b").device().unwrap().provenance + }; + assert_eq!(at(json!(false)), Provenance::Silicon); + assert_eq!(at(json!(true)), Provenance::Simulator("the service's b simulator".into())); + } } diff --git a/wai/wai-quantum-qpu/src/transport.rs b/wai/wai-quantum-qpu/src/transport.rs index 84fd88a53..5892f1485 100644 --- a/wai/wai-quantum-qpu/src/transport.rs +++ b/wai/wai-quantum-qpu/src/transport.rs @@ -12,11 +12,14 @@ use std::path::PathBuf; use std::process::{Command, Stdio}; use std::sync::atomic::{AtomicU64, Ordering}; -/// An HTTP method the clients use. +/// An HTTP method the clients use. A minor release may add one, so a `match` +/// outside this crate takes a wildcard arm. #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[non_exhaustive] pub enum Method { Get, Post, + Put, } impl Method { @@ -24,6 +27,7 @@ impl Method { match self { Method::Get => "GET", Method::Post => "POST", + Method::Put => "PUT", } } } @@ -46,6 +50,10 @@ impl Request { Request { method: Method::Post, url: url.into(), headers: Vec::new(), body: Some(body) } } + pub fn put(url: impl Into, body: Vec) -> Self { + Request { method: Method::Put, url: url.into(), headers: Vec::new(), body: Some(body) } + } + pub fn header(mut self, name: &str, value: impl Into) -> Self { self.headers.push((name.to_string(), value.into())); self diff --git a/wai/wai-quantum-qpu/tests/clients.rs b/wai/wai-quantum-qpu/tests/clients.rs index db65b8de1..fe62c25f1 100644 --- a/wai/wai-quantum-qpu/tests/clients.rs +++ b/wai/wai-quantum-qpu/tests/clients.rs @@ -16,6 +16,10 @@ use wai_quantum::quantum::{Circuit, Gateset}; use wai_quantum::quantum_fabric::sampling::{QPU_DEFAULT, reference_distribution, sample_kernel}; use wai_quantum::quantum_fabric::{FabricClass, Parity, Provenance}; use wai_quantum::quantum_toolchain::from_qasm; +use wai_quantum_qpu::cloud_tasks::{CloudTasks, Keys}; +use wai_quantum_qpu::job::{self, Progress, Target}; +use wai_quantum_qpu::ledger::Ledger; +use wai_quantum_qpu::registry::Kind; use wai_quantum_qpu::sampler_v2::SamplerV2; use wai_quantum_qpu::native_turns::NativeTurns; use wai_quantum_qpu::prx_circuits::PrxCircuits; @@ -307,6 +311,195 @@ impl TurnsService { } } +// --------------------------------------------------------------------------- +// Cloud tasks: OpenQASM 3 in a verbatim box on a processor's labelled qubits, +// results in the person's bucket, as one row a shot or as probabilities +// --------------------------------------------------------------------------- + +struct TasksService { + simulator: bool, + /// Answer probabilities rather than one row a shot. + probabilities: bool, + /// The processor's qubit labels, in the order of its coupling map's + /// indices, in a line. They move when it is recalibrated. + labels: Mutex>, + /// The session token a request must carry, sent and signed. + token: Option<&'static str>, + /// The service no longer describes the device. + device_gone: std::sync::atomic::AtomicBool, + tasks: Mutex>, +} + +/// A task as the service keeps it: where its results go, what they are, and +/// how far it has got. +struct Task { + bucket: String, + directory: String, + results: Value, + asked: u32, + cancelled: Option, +} + +const TASK_LABELS: [u64; 5] = [4, 7, 9, 12, 15]; +/// The region tasks are made in, which their ids name. +const TASK_REGION: &str = "us-west-1"; + +impl TasksService { + fn new(simulator: bool, probabilities: bool) -> TasksService { + TasksService { simulator, probabilities, labels: Mutex::new(TASK_LABELS.to_vec()), token: None, device_gone: Default::default(), tasks: Mutex::new(Vec::new()) } + } + + fn device(&self) -> Value { + let caps = if self.simulator { + json!({"paradigm": {"qubitCount": 34, "nativeGateSet": [], "connectivity": {}}, "service": {"deviceCost": {"price": "0.075", "unit": "minute"}, "shotsRange": [1, 100000], "updatedAt": "2026-11-05T00:00:00Z"}}) + } else { + let labels = self.labels.lock().unwrap(); + let mut graph = serde_json::Map::new(); + for (i, l) in labels.iter().enumerate() { + let near: Vec = [i.checked_sub(1), Some(i + 1)].into_iter().flatten().filter_map(|j| labels.get(j)).map(u64::to_string).collect(); + graph.insert(l.to_string(), json!(near)); + } + json!({"paradigm": {"qubitCount": labels.len(), "nativeGateSet": ["rx", "rz", "cz"], "connectivity": {"fullyConnected": false, "connectivityGraph": graph}}, "service": {"deviceCost": {"price": "0.000425", "unit": "shot"}, "shotsRange": [10, 50000], "updatedAt": "2026-11-05T00:00:00Z"}}) + }; + json!({"deviceName": if self.simulator { "SIM" } else { "Line" }, "providerName": "Lab", "deviceStatus": "ONLINE", "deviceType": if self.simulator { "SIMULATOR" } else { "QPU" }, "deviceCapabilities": caps.to_string()}) + } + + /// A task's id, as the service writes one: its region, an account, its number. + fn task_id(n: usize) -> String { + format!("arn:aws:braket:{TASK_REGION}:000000000000:quantum-task/{n}") // allow-name: a task's id, as the service gives it + } + + /// The bucket and the key a storage request names, in either addressing: + /// the bucket in the host, or, for a name with a dot, first in the path. + fn storage_object(url: &str) -> Option<(String, String)> { + let rest = url.strip_prefix("https://")?; + let (host, path) = rest.split_once('/')?; + let domain = format!("s3.{TASK_REGION}.amazonaws.com"); // allow-name: the storage service's endpoint + if host == domain { + let (bucket, key) = path.split_once('/')?; + return Some((bucket.to_string(), key.to_string())); + } + let bucket = host.strip_suffix(&format!(".{domain}"))?; + (!bucket.contains('.')).then(|| (bucket.to_string(), path.to_string())) + } + + fn answer(&self, req: &Request) -> Response { + let auth = req.header_value("Authorization").unwrap_or_default(); + if !auth.starts_with("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/") { // allow-name: the signature's literal + return Response::json(403, &json!({"message": "unsigned"})); + } + // A temporary key's token is sent, and is among what is signed. + if self.token.is_some() && (req.header_value("x-amz-security-token") != self.token || !auth.contains("x-amz-security-token")) { + return Response::json(403, &json!({"message": "no token"})); + } + if let Some((bucket, key)) = Self::storage_object(&req.url) { + if !auth.contains(&format!("/{TASK_REGION}/s3/aws4_request")) { + return Response::json(403, &json!({"message": "signed for another region or service"})); + } + let tasks = self.tasks.lock().unwrap(); + return match tasks.iter().find(|t| t.bucket == bucket && format!("{}/results.json", t.directory) == key) { + Some(t) => Response::json(200, &t.results), + None => Response::json(404, &json!({"Code": "NoSuchKey", "bucket": bucket, "key": key})), + }; + } + let path = req.url.strip_prefix("https://tasks.test").unwrap_or(""); + let task_of = |p: &str| -> Option { p.rsplit("%2F").next()?.trim_end_matches("/cancel").parse().ok() }; + match req.method { + Method::Get if path.starts_with("/device/") => { + if self.device_gone.load(std::sync::atomic::Ordering::Relaxed) { + return Response::json(404, &json!({"message": "no such device"})); + } + Response::json(200, &self.device()) + } + Method::Get if path.starts_with("/quantum-task/") => { + let mut tasks = self.tasks.lock().unwrap(); + let Some(t) = task_of(path).and_then(|n| tasks.get_mut(n)) else { return not_found(req) }; + t.asked += 1; + // Queued, then running, then done; a cancel ends it in two steps. + let status = match t.cancelled { + Some(at) if t.asked <= at + 1 => "CANCELLING", + Some(_) => "CANCELLED", + None if t.asked == 1 => "QUEUED", + None if t.asked == 2 => "RUNNING", + None => "COMPLETED", + }; + Response::json(200, &json!({"status": status, "outputS3Bucket": t.bucket, "outputS3Directory": t.directory})) + } + Method::Put if path.starts_with("/quantum-task/") && path.ends_with("/cancel") => { + let mut tasks = self.tasks.lock().unwrap(); + let Some(t) = task_of(path).and_then(|n| tasks.get_mut(n)) else { return not_found(req) }; + if t.asked < 3 && t.cancelled.is_none() { + t.cancelled = Some(t.asked); + } + Response::json(200, &json!({"cancellationStatus": "CANCELLING"})) + } + Method::Post if path == "/quantum-task" => { + let b = body_json(req); + let action: Value = serde_json::from_str(b["action"].as_str().unwrap()).unwrap(); + let source = action["source"].as_str().unwrap(); + let shots = b["shots"].as_u64().unwrap(); + let labels = self.labels.lock().unwrap().clone(); + let index = |q: &str| -> usize { + if let Some(l) = q.strip_prefix('$') { + labels.iter().position(|&x| x == l.parse::().unwrap()).expect("a processor's own label") + } else { + q.trim_start_matches("q[").trim_end_matches(']').parse().unwrap() + } + }; + let (mut width, mut body, mut measured) = (labels.len(), String::new(), Vec::new()); + for line in source.lines() { + let line = line.trim().trim_end_matches(';'); + if let Some(w) = line.strip_prefix("qubit[").and_then(|w| w.strip_suffix("] q")) { + width = w.parse().unwrap(); + } else if let Some((_, q)) = line.split_once(" = measure ") { + measured.push(index(q)); + } else if line.is_empty() || line.starts_with("OPENQASM") || line.starts_with("bit[") || line.starts_with("#pragma") || line == "box {" || line == "}" || line.starts_with("i ") { + continue; + } else { + let (gate, args) = line.split_once(' ').unwrap(); + let gate = if gate == "v" { "sx" } else { gate }; + let qubits: Vec = args.split(',').map(|q| format!("q[{}]", index(q.trim()))).collect(); + body.push_str(&format!("{gate} {};\n", qubits.join(", "))); + } + } + let p = physical_distribution(width, &body); + // The device's own numbers for the qubits measured: integers, as the + // service's published result schema has them (gate-model task result v1). + let named: Vec = measured.iter().map(|&i| if self.simulator { i as u64 } else { labels[i] }).collect(); + let mut tasks = self.tasks.lock().unwrap(); + let n = tasks.len(); + let id = Self::task_id(n); + let mut results = json!({ + "braketSchemaHeader": {"name": "braket.task_result.gate_model_task_result", "version": "1"}, // allow-name: the schema's name + "measuredQubits": named, + "taskMetadata": {"braketSchemaHeader": {"name": "braket.task_result.task_metadata", "version": "1"}, "id": id, "shots": shots, "deviceId": b["deviceArn"]}, // allow-name: the schema's name + "additionalMetadata": {"action": action}, + }); + if self.probabilities { + let mut probs = serde_json::Map::new(); + for (x, &px) in p.iter().enumerate().filter(|&(_, &px)| px > 1e-12) { + let bits: String = measured.iter().map(|&q| if (x >> q) & 1 == 1 { '1' } else { '0' }).collect(); + let e = probs.entry(bits).or_insert(json!(0.0)); + *e = json!(e.as_f64().unwrap() + px); + } + results["measurementProbabilities"] = Value::Object(probs); + } else { + let mut rows = Vec::new(); + for (reg, k) in register_counts(&p, &measured, shots) { + let row: Vec = (0..measured.len()).map(|i| (reg >> i) & 1).collect(); + rows.extend(std::iter::repeat_n(json!(row), k as usize)); + } + results["measurements"] = json!(rows); + } + let prefix = b["outputS3KeyPrefix"].as_str().unwrap(); + tasks.push(Task { bucket: b["outputS3Bucket"].as_str().unwrap().to_string(), directory: format!("{prefix}/{n}"), results, asked: 0, cancelled: None }); + Response::json(201, &json!({"quantumTaskArn": id})) + } + _ => not_found(req), + } + } +} + // --------------------------------------------------------------------------- // The tests // --------------------------------------------------------------------------- @@ -499,3 +692,282 @@ fn curl_carries_a_request_to_a_server_and_back() { assert!(head.iter().any(|h| h == "Authorization: Bearer t-1"), "{head:?}"); assert_eq!(body, b"{\"shots\":5}"); } + +// --------------------------------------------------------------------------- +// What each built-in kind states in its receipt +// --------------------------------------------------------------------------- + +const BELL: &str = "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n"; + +/// A transport that lives as long as the test, for a provider a target owns. +fn kept Response + 'static>(answer: F) -> &'static Scripted { + Box::leak(Box::new(Scripted::new(answer))) +} + +/// A Bell pair run on `provider` as a person runs it, confirmed, and sealed +/// with the published demo key (so that anyone can repeat these receipts). +fn sealed_run(kind: Kind, provider: Box) -> Value { + let t = Target::new("mine", kind, provider, None); + let req = json!({ "qasm": BELL, "shots": 400, "provider": "mine" }); + let key = wai_quantum::ed25519_dalek::SigningKey::from_bytes(&[42; 32]); + wai_quantum_qpu::job::run(&t, &req, true, &key, Wait { interval: std::time::Duration::ZERO, polls: 10 }, |_| {}).unwrap_or_else(|e| panic!("{e}")) +} + +/// The receipt keeps every rule a verifier checks, its counts' source and +/// class are the ones given, and what it says was sent is the body that was +/// posted: its members, and each setting at its place in it. With +/// `WAI_QUANTUM_RECEIPT_CORPUS` set to a directory, the body is written there +/// as `scripted-.json`, for the receipt corpus +/// (wai/quantum-conformance/receipts). +fn holds_to_what_was_sent(name: &str, body: &Value, posted: &Value, source: &str, class: &str) { + let c = wai_quantum::engine::job::check(body); + assert!(c.valid(), "{name}: {:?}\n{}", c.reasons, body["result"]); + let r = &body["result"]; + assert_eq!((r["counts_source"].as_str(), r["accuracy"]["class"].as_str()), (Some(source), Some(class)), "{name}"); + assert!(r["counts"].as_object().unwrap().keys().all(|k| k == "00" || k == "11"), "{name}: {}", r["counts"]); + let e = &r["execution"]; + assert_eq!(e["stated_by"], "client", "{name}"); + let mut fields: Vec<&str> = posted.as_object().unwrap().keys().map(String::as_str).collect(); + fields.sort_unstable(); + assert_eq!(e["sent"]["fields"], json!(fields), "{name}: the members stated are the members sent"); + for (k, v) in e["sent"]["settings"].as_object().unwrap() { + if k.starts_with('/') { + assert_eq!(posted.pointer(k), Some(v), "{name}: {k} is what was sent"); + } + } + if let Some(dir) = std::env::var_os("WAI_QUANTUM_RECEIPT_CORPUS") { + let path = std::path::Path::new(&dir).join(format!("scripted-{name}.json")); + std::fs::write(&path, format!("{}\n", serde_json::to_string_pretty(body).unwrap())).unwrap(); + } +} + +fn posted(requests: Vec) -> Value { + body_json(requests.iter().rev().find(|r| r.method == Method::Post && !r.url.contains("iam.test")).expect("a submission")) +} + +/// Each built-in kind, through a service that follows its contract, states +/// what its client sent (read from the very body it posts), where its counts +/// come from, and its class by its device's provenance; and its receipt keeps +/// every rule of wai/extensions/quantum-providers.md section 8.4. +#[test] +fn every_built_in_kind_states_what_it_sent_and_its_receipt_keeps_every_rule() { + let service = SamplerV2Service::new(); + let t = kept(move |r: &Request| service.answer(r)); + let client = SamplerV2::new(t, Secret::new("sampler-key"), "crn:test", "test_device").with_endpoints("https://sampler.test/api/v1", "https://iam.test/identity/token").with_clock(|| 1_000); + let body = sealed_run(Kind::SamplerV2, Box::new(client)); + holds_to_what_was_sent("sampler-v2", &body, &posted(t.requests()), "shots", "measured"); + let e = &body["result"]["execution"]; + assert_eq!((e["twirling"].as_str(), e["decoupling"].as_str(), e["mitigation"].as_str()), (Some("off"), Some("off"), Some("default"))); + + let service = PrxService { jobs: Mutex::new(HashMap::new()) }; + let t = kept(move |r: &Request| service.answer(r)); + let body = sealed_run(Kind::PrxCircuits, Box::new(PrxCircuits::new(t, Secret::new("prx-token"), "test-qc").with_base("https://prx.test/api/v1"))); + holds_to_what_was_sent("prx-circuits", &body, &posted(t.requests()), "vendor_counts", "measured"); + // It sends circuits and shots, and so sets nothing. + assert_eq!(body["result"]["execution"]["sent"], json!({ "fields": ["circuits", "shots"], "settings": {} })); + assert!(wai_quantum::engine::provider::ASPECTS.iter().all(|k| body["result"]["execution"][*k] == "default")); + + for (name, noise) in [("native-turns", None), ("native-turns-noise", Some("a-device"))] { + let service = TurnsService { jobs: Mutex::new(HashMap::new()) }; + let t = kept(move |r: &Request| service.answer(r)); + let mut client = NativeTurns::new(t, Secret::new("turns-key"), "simulator").with_base("https://turns.test/v0.4"); + if let Some(n) = noise { + client = client.with_noise(n); + } + let body = sealed_run(Kind::NativeTurns, Box::new(client)); + holds_to_what_was_sent(name, &body, &posted(t.requests()), "probabilities", "simulated"); + assert_eq!(body["result"]["execution"]["sent"]["settings"].get("/noise/model").and_then(Value::as_str), noise, "{name}"); + } + + for (name, simulator, probabilities, source, class) in [ + ("cloud-tasks-processor", false, false, "shots", "measured"), + ("cloud-tasks-processor-probabilities", false, true, "probabilities", "measured"), + ("cloud-tasks-simulator", true, false, "shots", "simulated"), + ] { + let service = TasksService::new(simulator, probabilities); + let t = kept(move |r: &Request| service.answer(r)); + let keys = Keys::new("AKIDEXAMPLE", Secret::new("wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"), None); + let arn = if simulator { "arn:test:simulator" } else { "arn:test:processor" }; + let client = CloudTasks::new(t, keys, "us-west-1", arn, "bucket").unwrap().with_base("https://tasks.test"); + let body = sealed_run(Kind::CloudTasks, Box::new(client)); + let sent = posted(t.requests()); + holds_to_what_was_sent(name, &body, &sent, source, class); + // A verbatim box is stated exactly when the program sent has one. + let action: Value = serde_json::from_str(sent["action"].as_str().unwrap()).unwrap(); + let boxed = action["source"].as_str().unwrap().lines().any(|l| l.starts_with("#pragma") && l.ends_with("verbatim")); + let e = &body["result"]["execution"]; + assert_eq!((boxed, e["verbatim"].as_bool(), e["sent"]["settings"].get("verbatim_box").is_some()), (!simulator, Some(!simulator), !simulator), "{name}"); + // The compiled program's text is in the receipt, beside its hash. + let c = &body["result"]["compiled"]; + assert_eq!(c["program"].as_str().unwrap(), blake3::hash(c["qasm3"].as_str().unwrap().as_bytes()).to_hex().as_str()); + } +} +fn task_keys(token: Option<&str>) -> Keys { + Keys::new("AKIDEXAMPLE", Secret::new("wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"), token.map(Secret::new)) +} + +#[test] +fn cloud_tasks_round_trip_through_their_contract() { + // Each bucket's name addresses its results: in the host, or, with a dot, in the path. + for (simulator, probabilities, token, bucket) in [(false, false, None, "results-bucket"), (false, true, Some("session-token"), "results.dotted-bucket"), (true, false, None, "results.dotted-bucket"), (true, true, Some("session-token"), "results-bucket")] { + let mut service = TasksService::new(simulator, probabilities); + service.token = token; + let transport = Scripted::new(|r: &Request| service.answer(r)); + let client = CloudTasks::new(&transport, task_keys(token), "us-west-1", "arn:test:device", bucket).unwrap().with_base("https://tasks.test"); + let s = no_wait(QpuSampler::new(client).unwrap()); + assert_eq!(s.device().provenance == Provenance::Silicon, !simulator); + exercise(&s); + // Every task walked through its states, and was read where its bucket's name puts it. + let reads: Vec = transport.requests().iter().filter(|r| r.url.ends_with("results.json")).map(|r| r.url.clone()).collect(); + assert!(!reads.is_empty() && reads.iter().all(|u| if bucket.contains('.') { u.starts_with(&format!("https://s3.us-west-1.amazonaws.com/{bucket}/")) } else { u.starts_with(&format!("https://{bucket}.s3.us-west-1.amazonaws.com/")) }), "{reads:?}"); // allow-name: the storage service's endpoint + // The keys are signed with, and nothing secret is in anything sent. + for r in transport.requests() { + let all = format!("{} {:?} {}", r.url, r.headers, String::from_utf8_lossy(r.body.as_deref().unwrap_or_default())); + assert!(!all.contains("EXAMPLEKEY"), "the secret went out in {}", r.url); + } + } +} + +/// A request a person sends and confirms, on a device that may then change. +const TWO_QUBITS: &str = "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nx q[0];\nc[0] = measure q[0];\nc[1] = measure q[1];\n"; + +/// The job sent by one process and collected by a later one, with `between` +/// done to the device while it waited: what it sealed. +fn collected_later(tag: &str, between: impl Fn(&TasksService)) -> Value { + let service: &'static TasksService = Box::leak(Box::new(TasksService::new(false, false))); + let transport = kept(move |r: &Request| service.answer(r)); + let target = || { + let client = CloudTasks::new(transport, task_keys(None), "us-west-1", "arn:test:device", "bucket").unwrap().with_base("https://tasks.test"); + Target::new("tasks", Kind::CloudTasks, Box::new(client), None) + }; + let dir = std::env::temp_dir().join(format!("wai-qpu-clients-{tag}-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let ledger = Ledger::at(&dir); + let req = json!({ "qasm": TWO_QUBITS, "shots": 100, "provider": "tasks" }); + let sent = job::start(&target(), &req, true, &ledger).unwrap_or_else(|e| panic!("{e}")); + between(service); + let mut back = ledger.get(sent.handle()).unwrap(); + let key = wai_quantum::ed25519_dalek::SigningKey::from_bytes(&[42; 32]); + let done = job::follow(&target(), &mut back, &key, &ledger, Wait { interval: std::time::Duration::ZERO, polls: 10 }, |_| {}).unwrap_or_else(|e| panic!("{e}")); + let _ = std::fs::remove_dir_all(&dir); + let Progress::Done(body) = done else { panic!("not done: {}", done.job()) }; + body +} + +#[test] +fn a_job_collected_later_is_read_through_the_qubits_it_was_sent_on() { + let unmoved = collected_later("unmoved", |_| {})["result"]["counts"].clone(); + // A qubit added below the ones it ran on moves every index after it. + let added = collected_later("added", |s| *s.labels.lock().unwrap() = vec![2, 4, 7, 9, 12, 15]); + assert_eq!(added["result"]["counts"], unmoved, "read through the labels of the day it was collected"); + // A qubit it ran on is gone from the device's list, and it still ran there. + let gone = collected_later("gone", |s| *s.labels.lock().unwrap() = vec![7, 9, 12, 15]); + assert_eq!(gone["result"]["counts"], unmoved); + // The device no longer described at all: the record says what is needed. + let unread = collected_later("unread", |s| s.device_gone.store(true, std::sync::atomic::Ordering::Relaxed)); + assert_eq!(unread["result"]["counts"], unmoved); +} + +#[test] +fn a_simulator_s_estimate_says_it_is_only_the_minimum() { + let service = TasksService::new(true, false); + let transport = kept(move |r: &Request| service.answer(r)); + let client = CloudTasks::new(transport, task_keys(None), "us-west-1", "arn:test:device", "bucket").unwrap().with_base("https://tasks.test"); + let t = Target::new("tasks", Kind::CloudTasks, Box::new(client), None); + let p = job::plan(&t, &json!({ "qasm": TWO_QUBITS, "shots": 100, "provider": "tasks" })).unwrap_or_else(|e| panic!("{e}")); + assert!(p.confirmation().contains("at least 0.003750 USD"), "{}", p.confirmation()); + assert_eq!(p.json["estimate"]["at_least"], true, "{}", p.json["estimate"]); +} + +/// A job recorded by the last release, and collected by this one: as that +/// release wrote its record, with what it did not yet write taken out. Its +/// receipt keeps every rule, and says no one stated what was sent. +#[test] +fn a_job_recorded_by_the_last_release_is_sealed_with_a_receipt_this_one_accepts() { + fn on(tag: &str, provider: Box, again: Box, kind: Kind, class: &str) { + let dir = std::env::temp_dir().join(format!("wai-qpu-clients-recorded-{tag}-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let ledger = Ledger::at(&dir); + let req = json!({ "qasm": TWO_QUBITS, "shots": 100, "provider": "mine" }); + let sent = job::start(&Target::new("mine", kind, provider, None), &req, true, &ledger).unwrap_or_else(|e| panic!("{tag}: {e}")); + let path = dir.join(format!("{}.json", sent.handle())); + let mut record: Value = serde_json::from_str(&std::fs::read_to_string(&path).unwrap()).unwrap(); + // What 0.4.1 wrote: wai/wai-quantum-qpu/src/job.rs at the release's commit. + for (at, member) in [("sealing", "execution"), ("sealing", "approximate"), ("sealing/compiled", "qasm3"), ("sealing/compiled", "qubit_names"), ("", "qubit_names")] { + let o = if at.is_empty() { record.as_object_mut() } else { record.pointer_mut(&format!("/{at}")).and_then(Value::as_object_mut) }; + o.unwrap().shift_remove(member); + } + std::fs::write(&path, record.to_string()).unwrap(); + let mut back = ledger.get(sent.handle()).unwrap(); + let key = wai_quantum::ed25519_dalek::SigningKey::from_bytes(&[42; 32]); + let done = job::follow(&Target::new("mine", kind, again, None), &mut back, &key, &ledger, Wait { interval: std::time::Duration::ZERO, polls: 10 }, |_| {}).unwrap_or_else(|e| panic!("{tag}: {e}")); + let _ = std::fs::remove_dir_all(&dir); + let Progress::Done(body) = done else { panic!("{tag}: not done: {}", done.job()) }; + let c = wai_quantum::engine::job::check(&body); + assert_eq!(c.reasons, Vec::<&str>::new(), "{tag}: {}", body["result"]); + let r = &body["result"]; + assert_eq!(r["execution"]["stated_by"], "nobody", "{tag}"); + assert_eq!(r["accuracy"]["class"], class, "{tag}"); + assert_eq!(r["compiled"]["program"].as_str().unwrap(), blake3::hash(r["compiled"]["qasm3"].as_str().unwrap().as_bytes()).to_hex().as_str(), "{tag}"); + } + let service: &'static SamplerV2Service = Box::leak(Box::new(SamplerV2Service::new())); + let t = kept(move |r: &Request| service.answer(r)); + let client = || -> Box { Box::new(SamplerV2::new(t, Secret::new("sampler-key"), "crn:test", "test_device").with_endpoints("https://sampler.test/api/v1", "https://iam.test/identity/token").with_clock(|| 1_000)) }; + on("sampler", client(), client(), Kind::SamplerV2, "measured"); + let service: &'static PrxService = Box::leak(Box::new(PrxService { jobs: Mutex::new(HashMap::new()) })); + let t = kept(move |r: &Request| service.answer(r)); + let client = || -> Box { Box::new(PrxCircuits::new(t, Secret::new("prx-token"), "test-qc").with_base("https://prx.test/api/v1")) }; + on("prx", client(), client(), Kind::PrxCircuits, "measured"); + let service: &'static TurnsService = Box::leak(Box::new(TurnsService { jobs: Mutex::new(HashMap::new()) })); + let t = kept(move |r: &Request| service.answer(r)); + let client = || -> Box { Box::new(NativeTurns::new(t, Secret::new("turns-key"), "simulator").with_base("https://turns.test/v0.4")) }; + on("turns", client(), client(), Kind::NativeTurns, "simulated"); +} + +/// A program is sealed on indices, and the receipt says which of the device's +/// qubits each one was on the day it was sent, as the body sent names them. +#[test] +fn a_receipt_names_the_device_s_qubits_as_the_program_was_sent_on_them() { + let service = TasksService::new(false, false); + let t = kept(move |r: &Request| service.answer(r)); + let client = CloudTasks::new(t, task_keys(None), "us-west-1", "arn:test:processor", "bucket").unwrap().with_base("https://tasks.test"); + let body = sealed_run(Kind::CloudTasks, Box::new(client)); + let names = body["result"]["compiled"]["qubit_names"].as_object().unwrap_or_else(|| panic!("{}", body["result"]["compiled"])); + let action: Value = serde_json::from_str(posted(t.requests())["action"].as_str().unwrap()).unwrap(); + let source = action["source"].as_str().unwrap().to_string(); + for q in body["result"]["compiled"]["final_layout"].as_array().unwrap() { + let name = names[&q.to_string()].as_str().unwrap(); + assert!(source.contains(&format!("measure ${name};")), "{name} in {source}"); + } + + let service = PrxService { jobs: Mutex::new(HashMap::new()) }; + let t = kept(move |r: &Request| service.answer(r)); + let body = sealed_run(Kind::PrxCircuits, Box::new(PrxCircuits::new(t, Secret::new("prx-token"), "test-qc").with_base("https://prx.test/api/v1"))); + let names = body["result"]["compiled"]["qubit_names"].as_object().unwrap_or_else(|| panic!("{}", body["result"]["compiled"])); + let sent = posted(t.requests()).to_string(); + for q in body["result"]["compiled"]["final_layout"].as_array().unwrap() { + let name = names[&q.to_string()].as_str().unwrap(); + assert!(sent.contains(name), "{name} in {sent}"); + } +} + +/// A task cancelled while it waits ends cancelled, through the service's two +/// steps, and its record says so. +#[test] +fn a_cloud_task_cancelled_while_it_waits_ends_cancelled() { + let service: &'static TasksService = Box::leak(Box::new(TasksService::new(false, false))); + let transport = kept(move |r: &Request| service.answer(r)); + let client = CloudTasks::new(transport, task_keys(None), "us-west-1", "arn:test:device", "results-bucket").unwrap().with_base("https://tasks.test"); + let t = Target::new("tasks", Kind::CloudTasks, Box::new(client), None); + let dir = std::env::temp_dir().join(format!("wai-qpu-clients-cancel-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let ledger = Ledger::at(&dir); + let mut ticket = job::start(&t, &json!({ "qasm": TWO_QUBITS, "shots": 100, "provider": "tasks" }), true, &ledger).unwrap_or_else(|e| panic!("{e}")); + let key = wai_quantum::ed25519_dalek::SigningKey::from_bytes(&[42; 32]); + let ended = job::cancel(&t, &mut ticket, &key, &ledger).unwrap_or_else(|e| panic!("{e}")); + assert_eq!(ended.job()["status"], "cancelled", "{}", ended.job()); + // The service's own word, asked again: cancelling, then cancelled. + let id = TasksService::task_id(0); + assert_eq!(t.provider.status(&id).unwrap(), wai_quantum_qpu::JobStatus::Ended { status: "cancelled".into(), reason: String::new() }); + let _ = std::fs::remove_dir_all(&dir); +} diff --git a/wai/wai-quantum-qpu/tests/providers.rs b/wai/wai-quantum-qpu/tests/providers.rs index e83cb6911..d65a0e862 100644 --- a/wai/wai-quantum-qpu/tests/providers.rs +++ b/wai/wai-quantum-qpu/tests/providers.rs @@ -211,7 +211,10 @@ fn a_provider_run_is_sealed_with_what_it_ran_and_verifies() { assert!(ok, "{body}"); assert_eq!(signer.as_str(), Some(key().verifying_key().as_bytes().iter().map(|b| format!("{b:02x}")).collect::().as_str())); let r = &body["result"]; - assert_eq!((r["method"].as_str(), r["accuracy"]["class"].as_str()), (Some("provider"), Some("measured"))); + // A simulator's counts are simulated, not measured; the reference says it + // drew them one shot at a time and ran the gates as written. + assert_eq!((r["method"].as_str(), r["accuracy"]["class"].as_str(), r["counts_source"].as_str()), (Some("provider"), Some("simulated"), Some("shots"))); + assert_eq!((r["execution"]["stated_by"].as_str(), r["execution"]["verbatim"].as_bool(), r["execution"]["mitigation"].as_str()), (Some("provider"), Some(true), Some("none")), "{}", r["execution"]); assert_eq!(r["job"]["polls"], 3); assert_eq!((r["provider"]["name"].as_str(), r["provider"]["protocol"].as_str(), r["provider"]["provenance"].as_str()), (Some("lab"), Some(PROTOCOL), Some("simulator:the wai-quantum exact simulator: the state vector in fixed point, sampled with the seed given"))); assert!(r["calibration"].as_str().unwrap().starts_with("reference-")); diff --git a/wai/wai-quantum-service/README.md b/wai/wai-quantum-service/README.md index 09b3746af..db996b0c9 100644 --- a/wai/wai-quantum-service/README.md +++ b/wai/wai-quantum-service/README.md @@ -145,7 +145,7 @@ Counts are always seeded samples. | `GET /v1/jobs/{id}` | the job: its status, the request as submitted, its result when it is done, and the digests it is kept under; `?wait=S` (at most 25) holds the answer until its status changes | | `GET /v1/jobs/{id}/events` | the job's status as server-sent events: a `status` event at each change, then one `job` event with the whole job when it ends | | `DELETE /v1/jobs/{id}` | cancel a queued or running job | -| `POST /v1/verify` | `{"request": ..., "result": ...}`: whether the result's receipt seals that request and that result | +| `POST /v1/verify` | `{"request": ..., "result": ...}`: whether the result's receipt seals that request and that result and every rule of [the provider protocol's section 8.4](../extensions/quantum-providers.md#84-what-a-verifier-checks) holds, read from the body's own text: `valid`, `reasons`, `key`, `form`, and what a provider's result says of itself | | `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)) | @@ -244,7 +244,7 @@ Every finished result carries a `receipt`: an Ed25519 signature, by the key `GET - digest = BLAKE3(`wai:quantum-service-receipt` ‖ 0x02 ‖ len(R) ‖ R ‖ len(S) ‖ S), each len a big-endian u64; - R and S are the RFC 8785 canonical texts (`"canon": "rfc8785"`) of the request and of the result without its `receipt`: keys sorted, no whitespace, every number written as ECMAScript writes the double it reads. -So a result verifies after any JSON reader has carried it, a browser's included. Whole numbers in a request must be within ±(2^53 − 1), so that every reader reads them alike. Receipts issued before the canonical form carry no `canon` and still verify (see `src/receipt.rs`). +So a result verifies after any JSON reader has carried it, a browser's included. Whole numbers in a request must be within ±(2^53 − 1), so that every reader reads them alike. Receipts issued before the canonical form carry no `canon` and still verify. The provider protocol's [§8](../extensions/quantum-providers.md#8-the-receipt) states the seal in full: the canonical text, the first form, how a job is read, the strict signature, and the reasons a verifier gives; [`quantum-conformance/receipts/`](../quantum-conformance/receipts/README.md) holds receipts and a second verifier written from that text. The receipt names its key, and `GET /.well-known/wai-quantum-receipt-keys` lists every key the service has signed with, each with its validity window. `wai-quantum-service rotate-key` retires the key: the old one stays listed with its window closed, and a transition statement naming both keys is signed by each ([docs/self-host.md](docs/self-host.md#the-receipt-keys)). diff --git a/wai/wai-quantum-service/schemas/service-tools.v1.json b/wai/wai-quantum-service/schemas/service-tools.v1.json index 20428c117..e06c0aa2e 100644 --- a/wai/wai-quantum-service/schemas/service-tools.v1.json +++ b/wai/wai-quantum-service/schemas/service-tools.v1.json @@ -38,7 +38,7 @@ "anyOf": [ { "type": "object", - "description": "Whether a result's receipt seals exactly that request and that result, and the key it names.", + "description": "Whether a result's receipt seals exactly that request and that result and every rule of its form holds, and the key it names.", "required": [ "valid" ], @@ -46,11 +46,36 @@ "valid": { "type": "boolean" }, + "reasons": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Each rule the receipt breaks (the provider protocol's section 8.4): none when it is valid." + }, + "form": { + "type": [ + "string", + "null" + ], + "description": "The receipt's form: rfc8785, or first." + }, + "provider": { + "type": "object", + "description": "For a provider's result, what it says of itself." + }, "key": { "type": [ "string", "null" ] + }, + "signer": { + "type": [ + "string", + "null" + ], + "description": "That key as a did:key." } } }, diff --git a/wai/wai-quantum-service/src/discovery.rs b/wai/wai-quantum-service/src/discovery.rs index 637bbf5ca..b40b44a91 100644 --- a/wai/wai-quantum-service/src/discovery.rs +++ b/wai/wai-quantum-service/src/discovery.rs @@ -282,9 +282,9 @@ pub fn openapi(server: &str) -> Value { "get": { "summary": "The job's status as server-sent events: `status` events ({\"id\", \"status\"}) as it changes, then one `job` event with the whole job when it ends, and the stream closes; a comment every 15 s keeps it open", "responses": { "200": { "description": "An event stream", "content": { "text/event-stream": {} } }, "404": { "description": "No such job" } } } }, "/v1/verify": { "post": { - "summary": "Whether a result's receipt seals that request and that result; an estimate is also re-run, and each of its resource receipts checked ({\"valid\", \"signed\", \"receipts\", \"reproduced\"}); a set's receipt is checked over its head, every item as its request gives it, each item's own receipt, the root and each path ({\"valid\", \"signed\", \"derived\", \"items_sealed\", \"root\"})", + "summary": "Whether a result's receipt seals that request and that result and every rule of its form holds ({\"valid\", \"reasons\", \"key\", \"form\"}); an estimate is also re-run, and each of its resource receipts checked ({\"valid\", \"signed\", \"receipts\", \"reproduced\"}); a set's receipt is checked over its head, every item as its request gives it, each item's own receipt, the root and each path ({\"valid\", \"signed\", \"derived\", \"items_sealed\", \"root\"})", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["request", "result"], "properties": { "request": { "$ref": "#/components/schemas/JobRequest" }, "result": { "type": "object" } } } } } }, - "responses": { "200": { "description": "{\"valid\": bool}" } } + "responses": { "200": { "description": "{\"valid\": bool, \"reasons\": [string]}" }, "400": { "description": "The body is not JSON, not a job, or has no receipt" } } } }, "/v1/critique": { "post": { "summary": "A program's measures and smells, each with its lines", diff --git a/wai/wai-quantum-service/src/http.rs b/wai/wai-quantum-service/src/http.rs index b3c85fa54..d69720d8b 100644 --- a/wai/wai-quantum-service/src/http.rs +++ b/wai/wai-quantum-service/src/http.rs @@ -87,7 +87,7 @@ use crate::catalog; use crate::jobs::{Key, Refused, Service, Work, check_key}; -use crate::receipt::{self, Signer}; +use crate::receipt::Signer; use axum::extract::{ConnectInfo, DefaultBodyLimit, Path, Query, State}; use axum::http::{HeaderMap, HeaderValue, Method, StatusCode}; use axum::response::sse::{Event, KeepAlive, Sse}; @@ -127,7 +127,7 @@ impl App { /// alone, no API keys: what tests use. #[cfg(test)] pub fn bare(service: Arc, signer: Arc, base: &str) -> App { - let keyset = json!({ "format": receipt::KEYS_FORMAT, "algorithm": "ed25519", "keys": [{ "key": signer.public_key(), "not_before_ms": null, "current": true }] }); + let keyset = json!({ "format": crate::receipt::KEYS_FORMAT, "algorithm": "ed25519", "keys": [{ "key": signer.public_key(), "not_before_ms": null, "current": true }] }); App { service, signer, trusted: vec![], base: base.into(), console: format!("{base}/console"), keyset, keys: None } } } @@ -276,6 +276,8 @@ fn job_bounds(request: &Value) -> Result<(), (u16, String)> { pub(crate) fn plan_job(app: &App, request: &Value) -> Result<(catalog::Plan, Work), (u16, String)> { let cap = app.service.config.job_mib << 20; if catalog::is_set(request) { + // The set's own request is sealed too: its sweep's values and every field. + wai_quantum::engine::job::exact_request(request).map_err(|e| (400, e))?; let items = sets::expand(request).map_err(|e| (400, e))?; for (i, it) in items.iter().enumerate() { job_bounds(&it.request).map_err(|(c, e)| (c, format!("item {i}: {e}")))?; @@ -558,10 +560,19 @@ async fn cancel(State(app): State, Path(id): Path) -> Response { } /// `{"request": ..., "result": ...}` with the result's own `receipt`: whether -/// the receipt seals that request and that result. An estimate is also -/// re-run, and every resource receipt in it checked. -async fn verify(State(app): State, ConnectInfo(peer): ConnectInfo, headers: HeaderMap, body: Result, axum::extract::rejection::JsonRejection>) -> Response { - let Ok(Json(v)) = body else { return error(400, "the body is not JSON") }; +/// the receipt seals that request and that result and every rule of the +/// provider protocol's section 8.4 holds, read from the body's own text +/// (`engine::job::check_text`): `valid`, `reasons`, `key`, `form`, and what a +/// provider's result says of itself. An estimate is also re-run, and every +/// resource receipt in it checked. +async fn verify(State(app): State, ConnectInfo(peer): ConnectInfo, headers: HeaderMap, body: Result) -> Response { + let Ok(bytes) = body else { return error(400, "the body is not JSON") }; + let Some(text) = std::str::from_utf8(&bytes).ok() else { return error(400, "the body is not JSON") }; + let Ok(v) = serde_json::from_str::(text) else { return error(400, "the body is not JSON") }; + // Read as every job's text is, whatever kind of job it is. + if v["result"]["format"] == wai_quantum::engine::estimate::FORMAT && let Some(reason) = wai_quantum::engine::job::text_refusal(text) { + return reply(200, json!({ "valid": false, "reasons": [reason] })); + } // An estimate: its seal, every resource receipt, and its request re-run // here, within an estimate's bounds. if v["result"]["format"] == wai_quantum::engine::estimate::FORMAT { @@ -574,19 +585,17 @@ async fn verify(State(app): State, ConnectInfo(peer): ConnectInfo error(code, &why), }; } - // A set: its receipt over its head, every item, the root and the paths. + // A set: its receipt over its head, every item, the root and the paths (§8.6). if v["result"]["format"] == sets::FORMAT { - return reply(200, sets::verify(&v)); + return reply(200, wai_quantum::engine::job::verdict_of_text(text).unwrap_or_else(|e| json!({ "valid": false, "error": e }))); } - let (Some(request), Some(result)) = (v.get("request"), v.get("result")) else { + let (Some(_), Some(result)) = (v.get("request"), v.get("result")) else { return error(400, "expected {\"request\": ..., \"result\": ...}, the result carrying its receipt"); }; - let Some(receipt) = result.get("receipt") else { return error(400, "the result carries no receipt") }; - let mut bare = result.clone(); - if let Some(o) = bare.as_object_mut() { - o.remove("receipt"); + if result.get("receipt").is_none() { + return error(400, "the result carries no receipt"); } - reply(200, json!({ "valid": receipt::verify(request, &bare, receipt), "key": receipt.get("key") })) + reply(200, wai_quantum::engine::job::check_text(text).to_json()) } /// A design check ([`crate::analysis`]): the engine's answer, or the refusal. @@ -611,7 +620,7 @@ pub(crate) async fn compile(app: &App, who: &str, request: Value) -> Result { diff --git a/wai/wai-quantum-service/src/mcp.rs b/wai/wai-quantum-service/src/mcp.rs index 3aa1cbf77..2f2b3fc37 100644 --- a/wai/wai-quantum-service/src/mcp.rs +++ b/wai/wai-quantum-service/src/mcp.rs @@ -28,7 +28,6 @@ use crate::catalog; use crate::discovery; use crate::http::App; -use crate::receipt; use serde_json::{Value, json}; use std::time::Duration; use wai_quantum::engine::guide::{self, Host}; @@ -287,8 +286,8 @@ fn tool_list() -> Value { { "name": "verify_quantum_receipt", "title": "Verify a result's receipt", - "description": "Whether a result's receipt (an Ed25519 signature by the service) seals exactly that request and that result. Pass the job's own `request` (as run_quantum_circuit or get_quantum_job returns it: a run here asks for its verdict, so the request it seals says so) and its result.", - "inputSchema": { "type": "object", "required": ["request", "result"], "properties": { "request": { "type": "object" }, "result": { "type": "object" } } }, + "description": "Whether a result's receipt (an Ed25519 signature) seals exactly that request and that result, and every rule of its form holds (`reasons` names what does not). Pass the job's own `request` (as run_quantum_circuit or get_quantum_job returns it: a run here asks for its verdict, so the request it seals says so) and its result.", + "inputSchema": { "type": "object", "properties": { "job": { "type": "string", "description": "The job's text as it was given (preferred: read as the protocol reads it, so a member named twice or a number past what a parsed job keeps is seen)" }, "request": { "type": "object" }, "result": { "type": "object" } } }, "annotations": { "readOnlyHint": true } }, { @@ -821,17 +820,21 @@ async fn call(app: &App, who: &str, name: &str, args: Value, base: &str) -> Valu }; tool_result(text, v, false) } + "verify_quantum_receipt" if args["job"].is_string() => { + // The job's text, read as the protocol's section 8.2 reads it. + let text = args["job"].as_str().unwrap_or_default(); + let v = wai_quantum::engine::job::verdict_of_text(text).unwrap_or_else(|_| wai_quantum::engine::job::verdict_of(&wai_quantum::engine::job::check_text(text))); + let line = if v["valid"] == true { "valid: the receipt seals exactly this request and this result, and every rule of its form holds".to_string() } else { format!("not valid: {}", v["reasons"]) }; + tool_result(line, v, false) + } "verify_quantum_receipt" => { let (request, result) = (&args["request"], &args["result"]); - let Some(r) = result.get("receipt") else { + if result.get("receipt").is_none() { return tool_result("the result carries no receipt".into(), json!({ "valid": false }), true); - }; - let mut bare = result.clone(); - if let Some(o) = bare.as_object_mut() { - o.remove("receipt"); } - let valid = receipt::verify(request, &bare, r); - tool_result(if valid { "valid: the receipt seals exactly this request and this result".into() } else { "not valid: the request, the result or the receipt was changed".into() }, json!({ "valid": valid, "key": r.get("key") }), false) + let c = wai_quantum::engine::job::check(&json!({ "request": request, "result": result })); + let text = if c.valid() { "valid: the receipt seals exactly this request and this result, and every rule of its form holds".to_string() } else { format!("not valid: {}", c.reasons.join(", ")) }; + tool_result(text, c.to_json(), false) } "quantum_service_info" => { let c = &app.service.config; @@ -1163,6 +1166,8 @@ mod tests { ("run_quantum_circuit", json!({ "qasm": "qubit[1] q; foo q[0];" }), true), ("get_quantum_job", json!({ "id": "none" }), true), ("verify_quantum_receipt", json!({ "request": body["request"], "result": body["result"] }), false), + // Given the job's text, its answer names the signer as a did:key too. + ("verify_quantum_receipt", json!({ "job": body.to_string() }), false), ("verify_quantum_receipt", json!({ "request": body["request"], "result": forged }), false), ("verify_quantum_receipt", json!({ "request": body["request"], "result": { "counts": {} } }), true), ("critique_qasm", json!({ "qasm": bell }), false), diff --git a/wai/wai-quantum-service/src/receipt.rs b/wai/wai-quantum-service/src/receipt.rs index 91438fa23..2d2605747 100644 --- a/wai/wai-quantum-service/src/receipt.rs +++ b/wai/wai-quantum-service/src/receipt.rs @@ -15,7 +15,7 @@ //! in time; its private half is moved aside to `.retired-`, which //! the owner may destroy, and the service signs only with the current key. -pub use wai_quantum::engine::receipt::{inexact_number, verify}; +pub use wai_quantum::engine::receipt::inexact_number; use serde_json::{Value, json}; use std::path::{Path, PathBuf}; use wai_quantum::ed25519_dalek::{Signature, Signer as _, SigningKey, Verifier as _, VerifyingKey}; @@ -49,8 +49,10 @@ impl Signer { } /// The receipt for `result` of `request`. - pub fn seal(&self, request: &Value, result: &Value) -> Value { - receipt::seal(&self.key, request, result) + /// `result` with its receipt, a whole number no double holds written as the + /// double sealed ([`receipt::seal_into`]). + pub fn seal_into(&self, request: &Value, result: &mut Value) { + receipt::seal_into(&self.key, request, result); } /// The key itself, for what seals receipts of its own form (an @@ -251,6 +253,7 @@ pub fn default_path() -> PathBuf { #[cfg(test)] mod tests { use super::*; + use wai_quantum::engine::receipt::verify; #[test] fn a_key_is_created_once_private_and_then_reused() { @@ -281,7 +284,7 @@ mod tests { assert_eq!(set.doc["keys"][0]["key"], old.public_key()); assert!(set.doc["keys"][0]["not_before_ms"].is_null() && set.doc["keys"][0]["current"] == true); let (req, res) = (json!({ "qasm": "OPENQASM 3.0;", "shots": 1 }), json!({ "counts": { "0": 1 } })); - let sealed = old.seal(&req, &res); + let sealed = receipt::seal(old.key(), &req, &res); let e = rotate(&path).unwrap(); let new = Signer::load(&path).unwrap(); assert_ne!(new.public_key(), old.public_key()); diff --git a/wai/wai-quantum-service/tests/verify.rs b/wai/wai-quantum-service/tests/verify.rs new file mode 100644 index 000000000..1d892a7a3 --- /dev/null +++ b/wai/wai-quantum-service/tests/verify.rs @@ -0,0 +1,98 @@ +//! The job service's verify, over HTTP and as an MCP tool, on the corpus of +//! job receipts: the verdict the manifest gives, every rule of the provider +//! protocol's section 8.4 and not the seal alone. + +use serde_json::{Value, json}; +use std::io::{Read, Write}; +use std::net::{TcpListener, TcpStream}; +use std::path::{Path, PathBuf}; +use std::process::{Child, Command, Stdio}; +use std::time::{Duration, Instant}; + +fn free_port() -> u16 { + TcpListener::bind("127.0.0.1:0").unwrap().local_addr().unwrap().port() +} + +/// One HTTP/1.1 exchange, the body as bytes: the status and the body. +fn http(port: u16, method: &str, path: &str, body: &[u8]) -> (u16, String) { + let mut s = TcpStream::connect(("127.0.0.1", port)).unwrap(); + s.set_read_timeout(Some(Duration::from_secs(60))).unwrap(); + write!(s, "{method} {path} HTTP/1.1\r\nHost: 127.0.0.1\r\nContent-Type: application/json\r\nAccept: application/json, text/event-stream\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", body.len()).unwrap(); + s.write_all(body).unwrap(); + let mut out = String::new(); + s.read_to_string(&mut out).unwrap(); + let status = out.split_whitespace().nth(1).and_then(|c| c.parse().ok()).unwrap_or(0); + (status, out.split_once("\r\n\r\n").map(|(_, b)| b.to_string()).unwrap_or_default()) +} + +fn start(dir: &Path, port: u16) -> Child { + let child = Command::new(env!("CARGO_BIN_EXE_wai-quantum-service")) + .arg("serve") + .env("WAI_QSVC_ADDR", format!("127.0.0.1:{port}")) + .env("WAI_QSVC_STORE", dir.join("store")) + .env("WAI_QSVC_KEY", dir.join("signing.key")) + .env("WAI_QSVC_JOB_GB", "2") + .env("WAI_QSVC_BUDGET_GB", "4") + .env("WAI_QSVC_WORKERS", "1") + .env("WAI_QSVC_ENERGY", "0") + .env("WAI_QSVC_RATE", "100000") + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .expect("the service starts"); + let t = Instant::now(); + while TcpStream::connect(("127.0.0.1", port)).is_err() || http(port, "GET", "/v1/health", b"").0 != 200 { + assert!(t.elapsed() < Duration::from_secs(60), "the service did not answer"); + std::thread::sleep(Duration::from_millis(50)); + } + child +} + +fn corpus() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/receipts") +} + +fn sorted(v: &Value) -> Vec { + let mut r: Vec = v.as_array().map(|a| a.iter().filter_map(Value::as_str).map(str::to_string).collect()).unwrap_or_default(); + r.sort(); + r +} + +#[test] +fn the_service_gives_every_receipt_the_verdict_the_corpus_gives_it() { + let dir = std::env::temp_dir().join(format!("wai-qsvc-verify-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + let port = free_port(); + let mut child = start(&dir, port); + let manifest: Value = serde_json::from_str(&std::fs::read_to_string(corpus().join("manifest.json")).unwrap()).unwrap(); + let mut wrong = Vec::new(); + for e in manifest["entries"].as_array().unwrap() { + let name = e["file"].as_str().unwrap(); + let want = sorted(&e["reasons"]); + let bytes = std::fs::read(corpus().join(name)).unwrap(); + // Over HTTP: its text, as it is. A body that is no job at all has its + // own refusal, 400, as it always has. + let (code, answer) = http(port, "POST", "/v1/verify", &bytes); + let alone = want.len() == 1 && ["unreadable", "not_a_job", "no_receipt"].contains(&want[0].as_str()); + let said: Value = serde_json::from_str(&answer).unwrap_or(Value::Null); + if !(alone && code == 400) && !(code == 200 && sorted(&said["reasons"]) == want && said["valid"].as_bool() == Some(want.is_empty())) { + wrong.push(format!("{name}: over HTTP, the manifest {want:?}, the service {code} {answer}")); + } + // As an MCP tool, given the job's text: every receipt whose bytes a string can hold + // (a tool's argument is a string, UTF-8 by its form). + let Ok(text) = String::from_utf8(bytes.clone()) else { continue }; + let call = json!({ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "verify_quantum_receipt", "arguments": { "job": text } } }); + let (_, answer) = http(port, "POST", "/mcp", call.to_string().as_bytes()); + let said: Value = serde_json::from_str(&answer).unwrap_or(Value::Null); + let s = &said["result"]["structuredContent"]; + let _ = alone; + if sorted(&s["reasons"]) != want || s["valid"].as_bool() != Some(want.is_empty()) { + wrong.push(format!("{name}: as a tool, the manifest {want:?}, the service {s}")); + } + } + let _ = child.kill(); + let _ = child.wait(); + let _ = std::fs::remove_dir_all(&dir); + assert!(wrong.is_empty(), "{} wrong:\n{}", wrong.len(), wrong.join("\n")); +} diff --git a/wai/wai-quantum-views/src/lib.rs b/wai/wai-quantum-views/src/lib.rs index 2ca304706..9c8f92b0c 100644 --- a/wai/wai-quantum-views/src/lib.rs +++ b/wai/wai-quantum-views/src/lib.rs @@ -48,15 +48,15 @@ pub fn call(request: &str) -> String { .to_string() } -/// Whether `job`'s receipt seals its request and its result: `{"valid", -/// "key", "signer"}` (the signer as a `did:key`), or `{"error"}` for what is -/// not a job with a receipt. +/// Whether `job`'s receipt seals its request and its result and every rule it +/// can show holds: `{"valid", "reasons", "key", "form", "signer"}` (the signer +/// as a `did:key`), or `{"error"}` for what is not a job with a receipt. pub fn verify(job: &Value) -> Value { + // Every rule of the receipt (§8.4, and §8.6 for a set), without what the + // engine checks of a set past its receipt (writing a sweep's programs out + // again), which would carry the whole engine into the page. match job::verify(job) { - Ok((valid, key)) => { - let signer = key.as_str().and_then(job::did_of); - json!({ "valid": valid, "key": key, "signer": signer }) - } + Ok(_) => job::verdict_of(&job::check(job)), Err(e) => json!({ "error": e }), } } diff --git a/wai/wai-quantum/README.md b/wai/wai-quantum/README.md index 542c44253..c810e25a4 100644 --- a/wai/wai-quantum/README.md +++ b/wai/wai-quantum/README.md @@ -1007,8 +1007,11 @@ Every document the job engine writes has a versioned JSON Schema, committed as d runs a job on a device, an account or a simulator the person brings, through the [open provider protocol](../extensions/quantum-providers.md) or a built-in client, its cost shown before it is submitted. `engine::provider` reads such a - job, sets the measured counts beside the circuit's exact distribution and - seals them, labelled `measured`; `engine::catalog` refuses a job that names a + job, sets the provider's counts beside the circuit's exact distribution and + seals them, labelled `measured` from a processor and `simulated` from a + simulator, with their source and what was set off at the provider; it also + holds a sealed result to the rules a verifier checks (`engine::provider::check`, + through `engine::job::check`); `engine::catalog` refuses a job that names a provider, so a host that reaches none never runs one on its own methods. ## License diff --git a/wai/wai-quantum/README.zh-CN.md b/wai/wai-quantum/README.zh-CN.md index 72a2b3b5c..ff68516c2 100644 --- a/wai/wai-quantum/README.zh-CN.md +++ b/wai/wai-quantum/README.zh-CN.md @@ -519,7 +519,7 @@ wai-quantum components # 引擎在本机所经过的每个组件,及 - 线路课程,从第一条线路一直到资源估算,每道练习都由引擎检查:[`course/`](course/README.md),可在 Studio 中学习,也可通过 `wai-quantum learn` 学习,提供英文和简体中文版本。 - 浏览器演示:;关于干涉与学习的课程:。 - 按主题整理的全部文档:[docs/README.zh-CN.md](docs/README.zh-CN.md)。 -- 你自己的量子硬件:[`wai-quantum-qpu`](../wai-quantum-qpu/bring-your-own-quantum.md) 通过[开放提供方协议](../extensions/quantum-providers.md)或内置客户端,在使用者自带的设备、账户或模拟器上运行作业,并在提交之前显示其费用。`engine::provider` 读取这样的作业,把实测计数与线路的精确分布并列,并加以封存,标注为 `measured`;`engine::catalog` 拒绝指定了提供方的作业,因此无法连接任何提供方的宿主绝不会用它自己的方法来运行这种作业。 +- 你自己的量子硬件:[`wai-quantum-qpu`](../wai-quantum-qpu/bring-your-own-quantum.md) 通过[开放提供方协议](../extensions/quantum-providers.md)或内置客户端,在使用者自带的设备、账户或模拟器上运行作业,并在提交之前显示其费用。`engine::provider` 读取这样的作业,把提供方给出的计数与线路的精确分布并列,并加以封存:来自处理器的标注为 `measured`,来自提供方模拟器的标注为 `simulated`,同时写明计数的来源(`counts_source`)以及在提供方处关闭了什么(`execution`);`engine::catalog` 拒绝指定了提供方的作业,因此无法连接任何提供方的宿主绝不会用它自己的方法来运行这种作业。 ## 许可证 diff --git a/wai/wai-quantum/VERSIONING.md b/wai/wai-quantum/VERSIONING.md index 1588f2a73..fadf1fcdd 100644 --- a/wai/wai-quantum/VERSIONING.md +++ b/wai/wai-quantum/VERSIONING.md @@ -14,7 +14,7 @@ replacement, are listed in [DEPRECATIONS.md](DEPRECATIONS.md). |---|---|---| | `wai-quantum`, the engine (Rust crate) | `wai/wai-quantum`, published on crates.io | 0.8.x | | `wai-quantum-cli`, the command line `wai-quantum` and its MCP server (Rust crate) | `wai/wai-quantum-cli`, published on crates.io | 0.2.x | -| `wai-quantum-qpu`, quantum processors and the person's own providers (Rust crate) | `wai/wai-quantum-qpu`, published on crates.io | 0.4.x | +| `wai-quantum-qpu`, quantum processors and the person's own providers (Rust crate) | `wai/wai-quantum-qpu`, published on crates.io | 0.5.x | | The job service, its HTTP API and its MCP server | `wai/wai-quantum-service`, at `https://wai.transaction.science/quantum-jobs` | 0.2.x | | `wai-quantum`, the Python package | `wai/wai-quantum-py` | 0.1.x | | The editor extension | `wai/tools/wai-quantum-editor` | 0.1.x | @@ -115,9 +115,11 @@ compile's result names `wai.quantum.compile-result/1`): Both are inside what the receipt seals, so neither can be changed or removed without the receipt failing. The receipt's own form did not change: it is an -Ed25519 signature over BLAKE3(`wai:quantum-service-receipt\x02` ‖ the request -‖ the result), each in its RFC 8785 canonical text, the result without its -`receipt` member. What changed is the canonical text of a new result, which +Ed25519 signature over BLAKE3(`wai:quantum-service-receipt\x02` ‖ u64(|Q|) ‖ +Q ‖ u64(|T|) ‖ T), Q and T the request's and the result's RFC 8785 canonical +texts, the result without its `receipt` member, and u64 a length as 8 bytes, +big-endian ([the provider protocol's §8.2](../extensions/quantum-providers.md#82-how-it-is-sealed-and-how-a-job-is-read) +states it in full, with what a verifier checks). What changed is the canonical text of a new result, which now holds two more members in their sorted places, `"engine":{"name":"wai-quantum","version":"…"}` and `"schema":"wai.quantum.job-result/1"`. A receipt issued before them seals a @@ -164,7 +166,15 @@ Some things hold in every release, whatever its number: canonical one). A receipt's signed bytes are never changed; a new form of receipt is a new form, named by its `canon` member or its domain, beside the old ones. The pinned fixtures and conformance corpora are never re-pinned - to make a test pass. + to make a test pass. One thing is stated, because the other reading is a + forgery: a receipt whose text writes a whole number no double holds is refused + (`inexact_number`, section 8.4 of the provider protocol), since its signature + covers the double and anyone can change the digits. Its signature still checks, + and written as that double's digits, or as its canonical text, it verifies. + The local hosts sealed such a receipt when given such a number: `qec` a seed + past 2^53, their compile, pulse, estimate and `@Seal` any number in a + request, and a set any number in its own request or its sweep. They refuse + it now. - **A result says how exact it is.** Each method's accuracy class keeps its meaning. A method may become more exact; it is never made less exact under the same class. @@ -184,7 +194,7 @@ before 1.0; these packages promise this: - **What holds already:** the public surface above; breaking changes only in a 0.y bump, with the changelog saying what to do instead; deprecation at least one release before removal, listed in [DEPRECATIONS.md](DEPRECATIONS.md); - the schemas' rules; and every receipt ever issued verifying. + the schemas' rules; and every receipt ever issued verifying, as stated above. - **What does not hold yet:** the support windows below. Before 1.0, a 0.y release may follow the last sooner than a year after it, and the previous 0.y series is fixed only where the changelog says so. Each package's @@ -229,7 +239,8 @@ The workflow `.github/workflows/wai-quantum-contract.yml`, on Linux: - **API compatibility:** `cargo-semver-checks`, at a pinned version, compares `wai-quantum`, `wai-quantum-cli` and `wai-quantum-qpu` with the last version - of each published on crates.io (today 0.6.16, 0.1.4 and 0.2.0). It reports every change it finds, and + of each published on crates.io (today 0.8.1, 0.2.4 and 0.4.1; `wai-quantum-qpu` is 0.5.0 here, ahead of + the last it published, for the breaking change the changelog names). It reports every change it finds, and fails only on a breaking change made without the version bump the rule above asks for (a 0.y bump before 1.0, a major bump after). - **The schemas:** every schema file reads, names itself and refers only to diff --git a/wai/wai-quantum/schemas/job-result.v1.json b/wai/wai-quantum/schemas/job-result.v1.json index d3bd353d4..4beb522d5 100644 --- a/wai/wai-quantum/schemas/job-result.v1.json +++ b/wai/wai-quantum/schemas/job-result.v1.json @@ -189,7 +189,7 @@ "anyOf": [ { "type": "object", - "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), the BLAKE3 of its OpenQASM 3 text, its counts of gates and swaps, its layouts, and the check it passed before it was sent.", + "description": "A provider job: the program sent, compiled exactly to the device's own gates: its basis (the device's gate set), its OpenQASM 3 text (qasm3) and that text's BLAKE3 (program), its counts of gates and swaps, its layouts, and the check it passed before it was sent. Results sealed before qasm3 carry the hash alone.", "required": [ "basis", "program" @@ -204,6 +204,18 @@ "hash": { "type": "string" }, + "qasm3": { + "type": "string", + "description": "The program sent, as OpenQASM 3, $i being index i of the coupling map the provider described; program is the BLAKE3 of its UTF-8 bytes." + }, + "qubit_names": { + "type": "object", + "additionalProperties": { + "type": "string", + "minLength": 1 + }, + "description": "The device's own name for each index the program uses, as it was when the job was sent, where a client sends it under those names." + }, "native_gates": { "type": "integer" }, @@ -385,6 +397,80 @@ "type": "object", "description": "A provider job: the provider's name, kind and protocol, the device, its provenance, and the hash of its description." }, + "counts_source": { + "enum": [ + "shots", + "vendor_counts", + "probabilities" + ], + "description": "A provider job: where its counts come from (wai/extensions/quantum-providers.md section 8.3): tallied from one record a shot (shots), the provider's own counts (vendor_counts), or rebuilt from the probabilities it returned by largest remainders, derived and not shot records (probabilities). Results sealed before it carry neither it nor execution." + }, + "execution": { + "type": "object", + "description": "A provider job: how the device was asked to run the program (section 8.3): who states it (stated_by: client, provider or nobody); what the client sent (sent: the request's members and the settings among them); each of mitigation, twirling, decoupling, remapping and optimisation (off, on, default when the request sets nothing for it, none when the device has no such step, unknown when nobody states it); whether the gates were to run as written (verbatim); and a note.", + "required": [ + "stated_by", + "mitigation", + "twirling", + "decoupling", + "remapping", + "optimisation", + "verbatim" + ], + "properties": { + "stated_by": { + "enum": [ + "client", + "provider", + "nobody" + ] + }, + "sent": { + "type": "object", + "required": [ + "fields", + "settings" + ], + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "type": "object" + } + }, + "additionalProperties": false + }, + "mitigation": { + "$ref": "#/$defs/execution_step" + }, + "twirling": { + "$ref": "#/$defs/execution_step" + }, + "decoupling": { + "$ref": "#/$defs/execution_step" + }, + "remapping": { + "$ref": "#/$defs/execution_step" + }, + "optimisation": { + "$ref": "#/$defs/execution_step" + }, + "verbatim": { + "type": [ + "boolean", + "null" + ] + }, + "note": { + "type": "string" + } + }, + "additionalProperties": false + }, "calibration": { "type": [ "string", @@ -642,6 +728,16 @@ } }, "$defs": { + "execution_step": { + "enum": [ + "off", + "on", + "default", + "none", + "unknown" + ], + "description": "What happened to one step: off or on (by a setting the client sent, or as the provider states), default (the request sets nothing for it, so the provider's default stands), none (the device has no such step), unknown (nobody states it)." + }, "accuracy": { "type": "object", "description": "What the method promises of the numbers it gave.", @@ -657,9 +753,14 @@ "bounded", "estimate", "heuristic", - "measured" + "measured", + "simulated" ], - "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a device returned, not reproducible by recomputation." + "description": "bit-exact: the same bits on every machine; exact: no approximation beyond double-precision rounding; single: single-precision arithmetic; bounded: a rigorous bound beside each value; estimate: a statistical estimate with its standard error; heuristic: an approximation with an estimate of its error, not a bound; measured: counts a processor returned through the person's provider, not reproducible by recomputation; simulated: counts a provider's simulator or emulator returned, measured on no device. Results sealed before simulated existed label a provider's simulator measured, and are read by its provenance." + }, + "approximate": { + "const": true, + "description": "A simulated result whose provider states its simulator is approximate." }, "note": { "type": "string" diff --git a/wai/wai-quantum/schemas/receipt.v2.json b/wai/wai-quantum/schemas/receipt.v2.json index 978c80e15..380c995f3 100644 --- a/wai/wai-quantum/schemas/receipt.v2.json +++ b/wai/wai-quantum/schemas/receipt.v2.json @@ -2,26 +2,26 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://git.transaction.science/Transaction-Science/open-standards/raw/branch/main/wai/wai-quantum/schemas/receipt.v2.json", "title": "wai.quantum.receipt/2", - "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) signed serde_json's own text under the domain ending \\x01; they still verify.", + "description": "A result's receipt: an Ed25519 signature, by the key it names, over digest = BLAKE3(\"wai:quantum-service-receipt\\x02\" ‖ len ‖ request ‖ len ‖ result), each of request and result in its RFC 8785 canonical text (the result without its receipt member), each length 8 bytes big-endian. Whatever members the request and the result hold are sealed, the result's engine and schema among them. The first receipts (no canon member, $defs/first) sealed their writer's compact text under the domain ending \\x01; they still verify. wai/extensions/quantum-providers.md section 8.2 states the seal in full, and section 8.4 what a verifier checks.", "type": "object", "required": ["alg", "canon", "key", "digest", "signature"], "properties": { "alg": { "const": "ed25519" }, "canon": { "const": "rfc8785", "description": "The canonical form the digest is taken over." }, - "key": { "type": "string", "minLength": 64, "maxLength": 64, "description": "The signing key, 32 bytes in hex." }, - "digest": { "type": "string", "minLength": 64, "maxLength": 64, "description": "The digest signed, 32 bytes in hex." }, - "signature": { "type": "string", "minLength": 128, "maxLength": 128, "description": "The signature, 64 bytes in hex." } + "key": { "type": "string", "minLength": 64, "maxLength": 64, "pattern": "^[0-9a-f]{64}$", "description": "The signing key, 32 bytes in lower-case hex." }, + "digest": { "type": "string", "minLength": 64, "maxLength": 64, "pattern": "^[0-9a-f]{64}$", "description": "The digest signed, 32 bytes in lower-case hex." }, + "signature": { "type": "string", "minLength": 128, "maxLength": 128, "pattern": "^[0-9a-f]{128}$", "description": "The signature, 64 bytes in lower-case hex." } }, "$defs": { "first": { - "description": "The first form, before canon: the digest is over serde_json's own text of each, under the domain ending \\x01. Never written now; still verified.", + "description": "The first form, before canon: the digest is over the compact text its writer wrote of each, members in the document's order (section 8.2 of wai/extensions/quantum-providers.md), under the domain ending \\x01. Never written now; still verified.", "type": "object", "required": ["alg", "key", "digest", "signature"], "properties": { "alg": { "const": "ed25519" }, - "key": { "type": "string", "minLength": 64, "maxLength": 64 }, - "digest": { "type": "string", "minLength": 64, "maxLength": 64 }, - "signature": { "type": "string", "minLength": 128, "maxLength": 128 } + "key": { "type": "string", "minLength": 64, "maxLength": 64, "pattern": "^[0-9a-f]{64}$" }, + "digest": { "type": "string", "minLength": 64, "maxLength": 64, "pattern": "^[0-9a-f]{64}$" }, + "signature": { "type": "string", "minLength": 128, "maxLength": 128, "pattern": "^[0-9a-f]{128}$" } } } } diff --git a/wai/wai-quantum/schemas/set.v1.json b/wai/wai-quantum/schemas/set.v1.json index 344087987..be83d9eeb 100644 --- a/wai/wai-quantum/schemas/set.v1.json +++ b/wai/wai-quantum/schemas/set.v1.json @@ -157,6 +157,26 @@ "string", "null" ] + }, + "signer": { + "type": [ + "string", + "null" + ], + "description": "That key as a did:key, where a surface gives it." + }, + "reasons": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Each rule the set's receipt breaks (the provider protocol's sections 8.4 and 8.6): none when it is valid." + }, + "form": { + "type": [ + "string", + "null" + ] } } } diff --git a/wai/wai-quantum/src/engine/answer/eval.rs b/wai/wai-quantum/src/engine/answer/eval.rs index 2ce04e744..acf64e7f1 100644 --- a/wai/wai-quantum/src/engine/answer/eval.rs +++ b/wai/wai-quantum/src/engine/answer/eval.rs @@ -227,7 +227,8 @@ impl Ev<'_> { let key = self.host.key.ok_or("no key here to seal with")?; let (request, result) = self.sealed(&items)?; let mut result = result; - result["receipt"] = receipt::seal(key, &request, &result); + crate::engine::job::exact_request(&request)?; + receipt::seal_into(key, &request, &mut result); Ok(json!({ "request": request, "result": result })) } "Equivalent" => { diff --git a/wai/wai-quantum/src/engine/compile.rs b/wai/wai-quantum/src/engine/compile.rs index d63c9e1ce..af27eb10e 100644 --- a/wai/wai-quantum/src/engine/compile.rs +++ b/wai/wai-quantum/src/engine/compile.rs @@ -1080,6 +1080,8 @@ pub fn call(request: &Value) -> Result { /// run after the level's own. An answer a registered pass took part in /// carries `components`, the identity of every component that made it. pub(crate) fn call_in(reg: &Registry, request: &Value) -> Result { + #[cfg(feature = "quantum_receipt")] + super::job::exact_request(request)?; let src = request.get("qasm").and_then(Value::as_str).ok_or("give \"qasm\": the program")?; let p = crate::qasm::parse(src)?; let o = Options::read(request)?; @@ -1110,8 +1112,7 @@ pub(crate) fn call_in(reg: &Registry, request: &Value) -> Result /// job's receipt takes, so [`super::job::verify`] checks it. #[cfg(feature = "quantum_receipt")] pub fn seal(request: &Value, mut result: Value, key: &ed25519_dalek::SigningKey) -> Value { - let r = super::receipt::seal(key, request, &result); - result["receipt"] = r; + super::receipt::seal_into(key, request, &mut result); json!({ "request": request, "result": result }) } diff --git a/wai/wai-quantum/src/engine/estimate/mod.rs b/wai/wai-quantum/src/engine/estimate/mod.rs index 8cc350a06..7f669d35f 100644 --- a/wai/wai-quantum/src/engine/estimate/mod.rs +++ b/wai/wai-quantum/src/engine/estimate/mod.rs @@ -654,6 +654,7 @@ impl Evaluation { /// [`super::job::verify`] checks it and [`verify`] re-runs it. #[cfg(feature = "quantum_receipt")] pub fn sealed(request: &Value, key: &ed25519_dalek::SigningKey) -> Result { + super::job::exact_request(request)?; let ev = evaluate(request)?; let did = crate::quantum_receipt::did_key(&key.verifying_key()); let unmetered = |run: &mut dyn FnMut()| { @@ -674,7 +675,7 @@ pub fn sealed(request: &Value, key: &ed25519_dalek::SigningKey) -> Result /// not yet sealed. A host that runs a job in a process of its own, away from /// its key, gives these back to be sealed ([`seal`]). pub fn plan_and_run(request: &Value, job_bytes: u64) -> Result<(catalog::Plan, Value), String> { - if let Some(at) = receipt::inexact_number(request) { - return Err(super::refusal::tag(super::refusal::LIMIT, format!("`{at}`: a whole number here must be within ±9007199254740991 (2^53 − 1), so that every JSON reader reads it alike and the receipt binds it"))); - } + exact_request(request)?; let plan = catalog::plan(request, job_bytes)?; let result = super::run::run(request, &plan)?; Ok((plan, result)) } +/// A request with a whole number past ±(2^53 − 1) refused, on every path to a +/// seal: a receipt seals the double, so the text would say more than was sealed +/// and a verifier would refuse it (§8.4 `inexact_number`). +pub fn exact_request(request: &Value) -> Result<(), String> { + if let Some(at) = receipt::inexact_number(request) { + return Err(super::refusal::tag(super::refusal::LIMIT, format!("`{at}`: a whole number here must be within ±9007199254740991 (2^53 − 1), so that every JSON reader reads it alike and the receipt binds it"))); + } + // Every other number, as this host writes it and as RFC 8785 does, is one a + // verifier reads as what was sealed: a host seals nothing its verifier refuses. + fn spelt_well(v: &Value) -> bool { + match v { + Value::Number(n) => whole_and_exact(&n.to_string()) && n.as_f64().is_none_or(|x| whole_and_exact(&receipt::es_number(x))), + Value::Array(a) => a.iter().all(spelt_well), + Value::Object(m) => m.values().all(spelt_well), + _ => true, + } + } + if !spelt_well(request) { + return Err(super::refusal::tag(super::refusal::LIMIT, "a number here is one no double holds as it is written, so the receipt could not bind it")); + } + Ok(()) +} + /// The job body for `result` of `request` under `plan`, in the service's /// form: `energy` inside the result, and the result sealed with `key`. pub fn seal(request: &Value, plan: &catalog::Plan, mut result: Value, key: &SigningKey, energy: Value) -> Value { result["energy"] = energy; - let seal = receipt::seal(key, request, &result); - result["receipt"] = seal; + receipt::seal_into(key, request, &mut result); json!({ "status": "done", "method": plan.method, @@ -45,17 +65,389 @@ pub fn seal(request: &Value, plan: &catalog::Plan, mut result: Value, key: &Sign } /// Whether a job body's result carries a receipt that seals that result and -/// its request, and the key the receipt names. +/// its request, and every rule of wai/extensions/quantum-providers.md §8.4 +/// that a parsed job can show holds ([`check`]); and the key the receipt names. +/// What only its text shows, a member named twice or a number past what a +/// parsed one keeps, is for [`check_text`], which a surface holding the text +/// calls instead. pub fn verify(job: &Value) -> Result<(bool, Value), String> { - let (Some(request), Some(result)) = (job.get("request"), job.get("result")) else { + let (Some(_), Some(result)) = (job.get("request"), job.get("result")) else { return Err("expected a job: {\"request\": ..., \"result\": ...}, the result carrying its receipt".into()); }; let Some(seal) = result.get("receipt") else { return Err("the result carries no receipt".into()) }; + Ok((check(job).valid(), seal.get("key").cloned().unwrap_or(Value::Null))) +} + +/// What a surface given a parsed job answers: every rule it can show +/// ([`check`], with `reasons`, `form` and what a provider's result says of +/// itself), and the signer as a `did:key`. +pub fn verdict(job: &Value) -> Result { + if is_set(job) { + return Ok(of_a_set(job, &check(job))); + } + verify(job)?; + Ok(verdict_of(&check(job))) +} + +/// A set's verdict: its reasons (§8.6), and beside them what the engine +/// checks of a set past its receipt (`sets::verify`: a sweep's every program +/// written out again at its point, `derived`). +fn of_a_set(job: &Value, c: &Check) -> Value { + let mut v = super::sets::verify(job); + let mut reasons = c.reasons.clone(); + reasons.sort_unstable(); + v["reasons"] = json!(reasons); + v["form"] = json!(c.form); + v["valid"] = json!(c.valid() && v["derived"] == true); + v["signer"] = json!(v["key"].as_str().and_then(did_of)); + v +} + +/// [`verdict`] of a job given as text, read as §8.2 reads it ([`check_text`]). +pub fn verdict_of_text(text: &str) -> Result { + let job: Value = serde_json::from_str(text).map_err(|e| format!("not JSON: {e}"))?; + if is_set(&job) { + let c = check_text(text); + return Ok(if c.reasons.first() == Some(&UNREADABLE) { verdict_of(&c) } else { of_a_set(&job, &c) }); + } + verify(&job)?; + Ok(verdict_of(&check_text(text))) +} + +/// A check as a surface gives it. +pub fn verdict_of(c: &Check) -> Value { + let mut v = c.to_json(); + v["signer"] = json!(c.key.as_deref().and_then(did_of)); + v +} + +/// The text is not one JSON value as §8.2 reads one: not UTF-8, a byte order +/// mark, a syntax error, a member named twice in one object, a lone +/// surrogate, a number a double cannot reach, nesting deeper than 127. +pub const UNREADABLE: &str = "unreadable"; +/// Not a job: not an object holding `request` and `result`, each an object. +pub const NOT_A_JOB: &str = "not_a_job"; +/// The result carries no `receipt`. +pub const NO_RECEIPT: &str = "no_receipt"; +/// A set's items (§8.6) are not of their form, not counted by its `count`, or +/// not the items its request gives. +pub const SET_ITEMS: &str = "set_items"; +/// An item of a set that ran is not a valid job: its own receipt, or a rule of +/// §8.4, fails. +pub const SET_ITEM: &str = "set_item"; +/// A set's `root` is not the Merkle root of its items' leaves, or an item's +/// `path` does not lead from its leaf to it. +pub const SET_ROOT: &str = "set_root"; +/// A whole number written in what a receipt seals (the request, and the result +/// less its receipt) that the receipt seals as a double and a double cannot +/// hold exactly: the text says more than what was sealed. A canonical receipt +/// seals every number as its double; a first-form receipt seals a whole number +/// from −2^63 to 2^64 − 1 by its digits, and every other by its double. +pub const INEXACT_NUMBER: &str = "inexact_number"; + +/// What a verifier finds of a job body (wai/extensions/quantum-providers.md +/// §8.4): every reason that applies, in the order of the reasons' table. It is +/// valid when there is none. +#[derive(Clone, Debug, Default, PartialEq)] +#[non_exhaustive] +pub struct Check { + /// The reasons, each named once. + pub reasons: Vec<&'static str>, + /// The key the receipt names, in hex, when it names one of its form. + pub key: Option, + /// `rfc8785`, or `first` for a receipt of the first form; `None` when the + /// receipt has no form a verifier reads. + pub form: Option<&'static str>, + /// For a provider's result, what it says of itself, as §8 asks a verifier + /// to state it: the class, whether its fields are stated, its counts' + /// source and how the device was asked to run the program. + pub provider: Option, +} + +impl Check { + pub fn valid(&self) -> bool { + self.reasons.is_empty() + } + + /// The check as JSON: `valid`, `reasons` (sorted), `key`, `form`, and + /// `provider` when the result is a provider's. + pub fn to_json(&self) -> Value { + let mut reasons = self.reasons.clone(); + reasons.sort_unstable(); + let mut v = json!({ "valid": self.valid(), "reasons": reasons, "key": self.key, "form": self.form }); + if let Some(p) = &self.provider { + v["provider"] = p.clone(); + } + v + } + + fn only(reason: &'static str) -> Check { + Check { reasons: vec![reason], ..Check::default() } + } + + fn push(&mut self, reason: &'static str) { + if !self.reasons.contains(&reason) { + self.reasons.push(reason); + } + } +} + +/// [`check_text`] of a job body given as bytes: `unreadable` when they are not +/// UTF-8. +pub fn check_bytes(bytes: &[u8]) -> Check { + std::str::from_utf8(bytes).map_or_else(|_| Check::only(UNREADABLE), check_text) +} + +/// [`check`] of a job body given as text, read as §8.2 reads it: strictly, +/// each number by its literal, so that a whole number a double cannot hold is +/// found even where a general reader would round it. +pub fn check_text(text: &str) -> Check { + let Ok(tree) = crate::ijson::parse_lenient(text.as_bytes()) else { return Check::only(UNREADABLE) }; + let Ok(v) = serde_json::from_str::(text) else { return Check::only(UNREADABLE) }; + let mut c = check(&v); + if c.reasons.first() != Some(&NOT_A_JOB) && c.reasons.first() != Some(&NO_RECEIPT) { + if inexact_literal(&tree, c.form == Some("first")) { + c.push(INEXACT_NUMBER); + } + // A set's items are jobs: one that writes a number its receipt did not seal is not valid. + if is_set(&v) + && let Some(crate::ijson::Json::Array(items)) = tree.get("result").and_then(|r| r.get("items")) + && items.iter().any(|it| { + // An item's own receipt's form; one that failed has none, and its + // leaf is the canonical form's digest, which seals the double. + let first = it.get("result").and_then(|r| r.get("receipt")).is_some_and(|r| r.get("canon").is_none()); + inexact_literal(it, first) + }) + { + c.push(SET_ITEM); + } + } + c +} + +/// Whether a job is a set's (§8.6). +fn is_set(job: &Value) -> bool { + job["result"]["format"] == super::sets::FORMAT +} + +/// What §8.2 finds of a job's text before anything else is checked, for a kind +/// of job with a verifier of its own (a set, an estimate, an algorithm's +/// answer): `unreadable`, or `inexact_number`. A surface holding the text asks +/// this first, as [`check_text`] does of a plain job. +pub fn text_refusal(text: &str) -> Option<&'static str> { + let Ok(tree) = crate::ijson::parse_lenient(text.as_bytes()) else { return Some(UNREADABLE) }; + if serde_json::from_str::(text).is_err() { + return Some(UNREADABLE); + } + let first = match tree.get("result") { + Some(crate::ijson::Json::Object(members)) => members.iter().find(|(k, _)| k == "receipt").is_some_and(|(_, r)| r.get("canon").is_none()), + _ => false, + }; + inexact_literal(&tree, first).then_some(INEXACT_NUMBER) +} + +/// Whether what a receipt seals, in a job's text, writes a whole number no +/// double holds; of the first form, one it did not write by its digits. +fn inexact_literal(tree: &crate::ijson::Json, first: bool) -> bool { + let inexact = |x: &crate::ijson::Json| x.any_number(|lit| !whole_and_exact(lit) && !(first && first_form_integer(lit))); + // A set's receipt seals its head: its items are each a job of their own. + let set = tree.get("result").and_then(|r| r.get("format")).and_then(|f| f.as_str()) == Some(super::sets::FORMAT); + let in_result = match tree.get("result") { + Some(crate::ijson::Json::Object(members)) => members.iter().any(|(k, x)| k != "receipt" && !(set && k == "items") && inexact(x)), + _ => false, + }; + tree.get("request").is_some_and(inexact) || in_result +} + +/// Whether a number's literal is a whole number written as one that the first +/// form's writer wrote by its digits: without a `-` from 0 to 2^64 − 1, with one +/// from −2^63 to −1 (§8.2). +fn first_form_integer(literal: &str) -> bool { + match literal.strip_prefix('-') { + Some(d) => d.bytes().all(|b| b.is_ascii_digit()) && d.parse::().is_ok_and(|v| (1..=1 << 63).contains(&v)), + None => literal.bytes().all(|b| b.is_ascii_digit()) && literal.parse::().is_ok(), + } +} + +/// Whether a number's literal is not a whole number written as one (with no +/// fraction and no exponent), or is one a double holds exactly, or is the +/// canonical text of the double it reads as (§8.2: RFC 8785 writes a whole +/// double past 2^53 by its shortest digits and zeros, 2^60 as +/// 1152921504606847000), which is that double's one spelling. +fn whole_and_exact(literal: &str) -> bool { + let digits = literal.strip_prefix('-').unwrap_or(literal); + if !digits.bytes().all(|b| b.is_ascii_digit()) { + return true; + } + // The nearest double, written out in full, is the literal when it is exact. + let x: f64 = digits.parse().unwrap_or(f64::INFINITY); + x.is_finite() && (format!("{x:.0}") == digits || receipt::es_number(x) == digits) +} + +/// What is wrong with a job body (§8.4), and what its provider's result says +/// of itself. Every receipt sealed by a host or the service is checked so: the +/// seal (`receipt::check`), and the rules a provider's result keeps +/// (`provider::check`). Numbers here are already doubles, so a whole number a +/// double cannot hold is found only past `u64` and `i64`; [`check_text`] finds +/// every one. +pub fn check(job: &Value) -> Check { + let (Some(request), Some(result)) = (job.get("request").filter(|r| r.is_object()), job.get("result").filter(|r| r.is_object())) else { + return Check::only(NOT_A_JOB); + }; + let Some(seal) = result.get("receipt") else { + // Nothing to check it against, but the result still says what it is. + let mut c = Check::only(NO_RECEIPT); + c.provider = super::provider::check(request, result).1; + return c; + }; + let set = is_set(job); + let mut c = if set { check_set(request, result, seal) } else { check_parts(request, result, seal) }; + // Read as doubles already, the numbers a first-form receipt sealed as doubles + // are past what this can see: `check_text` finds them. + let sealed_result = result.as_object().is_some_and(|o| o.iter().any(|(k, x)| k != "receipt" && !(set && k == "items") && receipt::inexact_double(x).is_some())); + if c.form != Some("first") && (receipt::inexact_double(request).is_some() || sealed_result) { + c.push(INEXACT_NUMBER); + } + c +} + +fn hex(b: &[u8]) -> String { + b.iter().map(|x| format!("{x:02x}")).collect() +} + +/// JSON equality, numbers by their value and members in any order. +fn same(a: &Value, b: &Value) -> bool { + match (a, b) { + (Value::Number(x), Value::Number(y)) => x.as_f64() == y.as_f64(), + (Value::Array(x), Value::Array(y)) => x.len() == y.len() && x.iter().zip(y).all(|(p, q)| same(p, q)), + (Value::Object(x), Value::Object(y)) => x.len() == y.len() && x.iter().all(|(k, p)| y.get(k).is_some_and(|q| same(p, q))), + _ => a == b, + } +} + +/// A set (§8.6): its receipt seals its request and its head (the result less +/// `items` and `receipt`); each item that ran is a job of its own; the items +/// are the ones its request gives, `count` of them; and each item's leaf leads +/// by its path to the head's `root`. +fn check_set(request: &Value, result: &Value, seal: &Value) -> Check { + let head = super::sets::head(result); + let mut c = Check::default(); + for r in receipt::check(request, &head, seal) { + c.push(r); + } + if !c.reasons.contains(&receipt::RECEIPT_FORM) { + c.key = seal.get("key").and_then(Value::as_str).map(str::to_string); + c.form = Some(if seal.get("canon").is_some() { "rfc8785" } else { "first" }); + } + let hex64 = |v: &Value| v.as_str().is_some_and(|s| s.len() == 64 && s.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))); + let Some(items) = result.get("items").and_then(Value::as_array).filter(|items| { + !items.is_empty() && items.iter().enumerate().all(|(i, it)| { + it.get("index").and_then(Value::as_f64) == Some(i as f64) + && it.get("request").is_some_and(Value::is_object) + && (it.get("result").is_some() != it.get("error").is_some()) + && it.get("result").is_none_or(Value::is_object) + && it.get("error").is_none_or(Value::is_string) + && it.get("path").and_then(Value::as_array).is_some_and(|p| p.iter().all(hex64)) + }) + }) else { + c.push(SET_ITEMS); + return c; + }; + // The items the request gives: its fields but programs, sweep and dry_run, + // shared by every item, each program's over them. + let mut shared = request.as_object().cloned().unwrap_or_default(); + for k in ["programs", "sweep", "dry_run"] { + shared.remove(k); + } + let given = match (request.get("programs").filter(|p| !p.is_null()), request.get("sweep").filter(|s| !s.is_null())) { + (Some(programs), None) => programs.as_array().is_some_and(|ps| { + ps.len() == items.len() + && ps.iter().zip(items).all(|(p, it)| { + let mut want = shared.clone(); + match p { + Value::String(q) => { + want.insert("qasm".into(), json!(q)); + } + Value::Object(o) => { + for (k, v) in o { + want.insert(k.clone(), v.clone()); + } + } + _ => return false, + } + same(&Value::Object(want), &it["request"]) + }) + }), + (None, Some(sweep)) => sweep.as_object().is_some_and(|grid| { + let points = grid.values().try_fold(1usize, |n, v| v.as_array().filter(|l| !l.is_empty()).and_then(|l| n.checked_mul(l.len()))); + points == Some(items.len()) + && items.iter().all(|it| { + // Each point's program is the request's written out there: the rest is shared. + let (mut rest, mut base) = (it["request"].as_object().cloned().unwrap_or_default(), shared.clone()); + base.remove("qasm"); + rest.remove("qasm").is_some_and(|q| q.is_string()) && same(&Value::Object(rest), &Value::Object(base)) + }) + }), + _ => false, + }; + if !given || result.get("count").and_then(Value::as_f64) != Some(items.len() as f64) { + c.push(SET_ITEMS); + } + // Each item that ran is one job, sealed by the set's key; one that failed + // writes no number its leaf could not seal. + let item_bad = |it: &Value| match it.get("result") { + Some(r) => { + let own = check(&json!({ "request": it["request"], "result": r })); + !own.valid() || is_set(&json!({ "result": r })) || own.key.as_deref() != seal.get("key").and_then(Value::as_str) + } + // Its leaf is the canonical form's digest whatever the set's form: it seals the double. + None => receipt::inexact_double(&it["request"]).is_some(), + }; + if items.iter().any(item_bad) { + c.push(SET_ITEM); + } + let leaves: Vec<[u8; 32]> = items + .iter() + .map(|it| match (it.get("result"), it.get("error").and_then(Value::as_str)) { + (Some(r), _) => super::sets::leaf(&it["request"], &Ok(r.clone())), + (None, Some(e)) => super::sets::leaf(&it["request"], &Err(e.to_string())), + (None, None) => [0; 32], + }) + .collect(); + let root_ok = hex64(&result["root"]) && result["root"].as_str() == Some(hex(&crate::merkle::merkle_root(&leaves)).as_str()); + let paths_ok = || { + let root = super::sets::unhex32(result["root"].as_str().unwrap_or_default()); + items.iter().zip(&leaves).enumerate().all(|(i, (it, leaf))| { + let path: Option> = it["path"].as_array().and_then(|p| p.iter().map(|h| h.as_str().and_then(super::sets::unhex32)).collect()); + matches!((root, path), (Some(root), Some(path)) if crate::merkle::merkle_check(leaf, i, items.len(), &path, &root)) + }) + }; + if !root_ok || !paths_ok() { + c.push(SET_ROOT); + } + c +} + +fn check_parts(request: &Value, result: &Value, seal: &Value) -> Check { let mut bare = result.clone(); if let Some(o) = bare.as_object_mut() { - o.remove("receipt"); + // Order kept: the first form's text is in the document's order. + o.shift_remove("receipt"); } - Ok((receipt::verify(request, &bare, seal), seal.get("key").cloned().unwrap_or(Value::Null))) + let mut c = Check::default(); + for r in receipt::check(request, &bare, seal) { + c.push(r); + } + if !c.reasons.contains(&receipt::RECEIPT_FORM) { + c.key = seal.get("key").and_then(Value::as_str).map(str::to_string); + c.form = Some(if seal.get("canon").is_some() { "rfc8785" } else { "first" }); + } + let (reasons, said) = super::provider::check(request, &bare); + for r in reasons { + c.push(r); + } + c.provider = said; + c } /// The `did:key` of a key given as 64 hex digits, as a receipt names it. @@ -170,6 +562,46 @@ pub(crate) mod tests { assert!(verify(&json!({ "request": {} })).is_err()); } + /// A job's text is read strictly, as §8.2 has it: a member named twice, a + /// lone surrogate or a number no double reaches makes it unreadable, and a + /// whole number a double cannot hold says more than the receipt sealed. + #[test] + fn a_job_s_text_is_read_strictly_and_its_numbers_by_their_literals() { + let key = SigningKey::from_bytes(&[3; 32]); + let sealed = |req: Value, res: Value| { + let mut result = res.clone(); + result["receipt"] = receipt::seal(&key, &req, &res); + json!({ "request": req, "result": result }) + }; + let req = json!({ "qasm": "OPENQASM 3.0; qubit[1] q; x q[0];", "shots": 10 }); + let body = sealed(req.clone(), json!({ "method": "statevector", "qubits": 1, "gates": 1, "accuracy": { "class": "exact" }, "work": 9007199254740992u64 })); + let text = body.to_string(); + assert!(check_text(&text).valid(), "{:?}", check_text(&text).reasons); + // 2^53 + 1 seals as 2^53: the text says more than was sealed. + let more = text.replace("9007199254740992", "9007199254740993"); + assert_eq!(check_text(&more).reasons, vec![INEXACT_NUMBER]); + assert_eq!(check(&serde_json::from_str(&more).unwrap()).reasons, vec![INEXACT_NUMBER]); + // The same double, spelt otherwise, is the same sealed value. + assert!(check_text(&text.replace("9007199254740992", "9.007199254740992e15")).valid()); + let dup = text.replacen("\"shots\":10", "\"shots\":10,\"shots\":10", 1); + assert_ne!(dup, text); + assert_eq!(check_text(&dup).reasons, vec![UNREADABLE]); + assert_eq!(check_text("{\"request\": {\"x\": \"\\ud800\"}}").reasons, vec![UNREADABLE]); + assert_eq!(check_text("{\"request\": 1e400}").reasons, vec![UNREADABLE]); + assert_eq!(check_text(&format!("{}{}", "[".repeat(128), "]".repeat(128))).reasons, vec![UNREADABLE]); + assert_eq!(check_text("[]").reasons, vec![NOT_A_JOB]); + assert_eq!(check_text("{\"request\": {}, \"result\": {}}").reasons, vec![NO_RECEIPT]); + // A noncharacter is a character like any other, and seals as one. + let nc = sealed(json!({ "qasm": "OPENQASM 3.0; // \u{fffe}" }), json!({ "method": "statevector", "accuracy": { "class": "exact" } })); + assert!(check_text(&nc.to_string()).valid()); + // Nothing outside the request and the result is sealed, and a verdict + // takes nothing from it. + let mut outer = body.clone(); + outer["why"] = json!("anything at all"); + outer["method"] = json!("a different method"); + assert!(check(&outer).valid()); + } + #[test] fn lint_gives_the_shape_or_the_line_of_the_problem() { let ok = lint("OPENQASM 3.0; qubit[3] q; bit[3] c; h q; c = measure q;"); @@ -333,4 +765,158 @@ pub(crate) mod tests { let e = plan_and_run(&json!({ "qasm": "qubit[1] q; rx(0.3) q[0];", "method": "exact" }), 1 << 30).unwrap_err(); assert_eq!(super::super::refusal::codes_in(&e), ["Q500", "J011"], "{e}"); } + + fn sealed_by(key: &SigningKey, req: Value, res: Value) -> Value { + let mut result = res.clone(); + result["receipt"] = receipt::seal(key, &req, &res); + json!({ "request": req, "result": result }) + } + + /// `verify` is what every surface given a parsed job calls: it holds every + /// rule that can be seen in one, as `check` does. + #[test] + fn verify_holds_what_check_holds() { + let key = SigningKey::from_bytes(&[3; 32]); + let res = json!({ "method": "statevector", "accuracy": { "class": "exact" } }); + let inexact = sealed_by(&key, json!({ "qasm": "x", "note": 9007199254740993u64 }), res.clone()); + assert_eq!(check(&inexact).reasons, vec![INEXACT_NUMBER]); + assert!(!verify(&inexact).unwrap().0, "a number no one signed"); + let string = sealed_by(&key, json!("a request that is no object"), res.clone()); + assert_eq!(check(&string).reasons, vec![NOT_A_JOB]); + assert!(!verify(&string).unwrap().0); + let claim = sealed_by(&key, json!({ "qasm": "x" }), json!({ "method": "statevector", "accuracy": { "class": "measured" } })); + assert!(!verify(&claim).unwrap().0); + } + + /// What a host seals, its own verifier accepts: a request holding a number + /// no double holds is refused where the request is read, on every path to + /// a seal, with the sentence a run gives. + #[test] + fn a_host_never_seals_what_its_verifier_refuses() { + const BIG: u64 = 1793907034945123456; + let key = SigningKey::from_bytes(&[3; 32]); + let holds = |what: &str, answer: Result| match answer { + Err(e) => assert!(e.contains("9007199254740991"), "{what}: refused for another reason: {e}"), + Ok(body) => assert_eq!(check(&body).reasons, Vec::<&str>::new(), "{what} sealed what its verifier refuses"), + }; + let qec = |op: &str, req: Value| super::super::qec::call(op, &req).map(|a| super::super::qec::seal(&req, a, &key)); + holds("decode", qec("decode", json!({ "memory": { "distance": 3, "rounds": 3, "p": 0.004 }, "shots": 20, "seed": BIG }))); + holds("threshold", qec("threshold", json!({ "family": "repetition", "distances": [3], "p": [0.05], "shots": 20, "seed": BIG }))); + holds("code", qec("code", json!({ "code": "surface:3", "note": BIG }))); + let req = json!({ "qasm": "OPENQASM 3.0;\nqubit[2] q;\nh q[0];\ncx q[0], q[1];\n", "note": BIG }); + holds("compile", super::super::compile::call(&req).map(|a| super::super::compile::seal(&req, a, &key))); + holds("estimate", super::super::estimate::sealed(&json!({ "qasm": "OPENQASM 3.0;\nqubit q;\nrz(0.3) q;\n", "budget": 1e-3, "note": BIG }), &key)); + holds("a run", run(&json!({ "qasm": "OPENQASM 3.0; qubit q; x q;", "shots": 4, "seed": BIG }), 1 << 30, &key, json!({ "unmetered": "a test" }))); + let req = json!({ "qasm": "OPENQASM 3.0;\nqubit q;\nx q;\n", "note": BIG }); + holds("a pulse schedule", super::super::pulse::call("schedule", &req).map(|a| super::super::pulse::seal(&req, a, &key))); + } + + /// A receipt sealed before the rule, with such a number, stays refused and + /// its signature stays good: what it sealed is the double, and written as + /// that double it verifies. + #[test] + fn a_receipt_sealed_with_a_number_no_double_holds_verifies_written_as_what_was_sealed() { + let key = SigningKey::from_bytes(&[3; 32]); + const BIG: u64 = 1793907034945123456; + let body = sealed_by(&key, json!({ "code": "surface:3", "seed": BIG }), json!({ "seed": BIG, "accuracy": { "class": "exact" } })); + let text = body.to_string(); + assert_eq!(check_text(&text).reasons, vec![INEXACT_NUMBER], "the signature holds, and only the rule is broken"); + let written = text.replace(&BIG.to_string(), &format!("{:.0}", BIG as f64)); + assert_eq!(check_text(&written).reasons, Vec::<&str>::new()); + } + + /// Reading a job's text costs what its length does: a big object is not + /// scanned once for each member. + #[test] + fn a_large_object_is_read_in_time_proportional_to_it() { + let mut members: Vec = (0..200_000).map(|i| format!("\"m{i:06}\":1")).collect(); + members.push("\"m000000\":2".to_string()); + let text = format!("{{\"request\":{{{}}},\"result\":{{}}}}", members.join(",")); + let t = std::time::Instant::now(); + assert_eq!(check_text(&text).reasons, vec![UNREADABLE]); + assert!(t.elapsed() < std::time::Duration::from_secs(20), "{:?}", t.elapsed()); + } + + /// A set seals its head, and a surface that takes any job takes a set: it is + /// checked as one, and a member named twice in its text is found. + #[test] + fn a_surface_that_takes_a_job_verifies_a_set_as_a_set() { + let key = SigningKey::from_bytes(&[3; 32]); + let body = super::super::sets::run(&json!({ "programs": ["OPENQASM 3.0; qubit q; x q;"], "shots": 8 }), 1 << 30, &key, json!({ "unmetered": "a test" })).unwrap(); + assert_eq!(verdict(&body).unwrap()["valid"], true, "{}", verdict(&body).unwrap()); + assert_eq!(verdict_of_text(&body.to_string()).unwrap()["valid"], true); + let text = body.to_string(); + let at = text.find("\"result\":{").unwrap() + "\"result\":{".len(); + let twice = format!("{}\"format\":7,{}", &text[..at], &text[at..]); + assert_eq!(verdict_of_text(&twice).unwrap()["reasons"], json!(["unreadable"])); + let mut changed = body.clone(); + changed["result"]["items"][0]["result"]["counts"] = json!({ "0": 8 }); + assert_eq!(verdict(&changed).unwrap()["valid"], false); + } + + /// A number no double holds in a result is written as the double sealed, so + /// what a host gives is what was sealed. + #[test] + fn a_result_s_number_no_double_holds_is_written_as_the_double_sealed() { + let key = SigningKey::from_bytes(&[3; 32]); + let req = json!({ "qasm": "x" }); + let mut result = json!({ "accuracy": { "class": "exact" }, "work": 1793907034945123456u64, "deep": [{ "n": -9007199254740993i64 }], "small": 12 }); + receipt::seal_into(&key, &req, &mut result); + let body = json!({ "request": req, "result": result }); + assert_eq!(check(&body).reasons, Vec::<&str>::new()); + assert_eq!(check_text(&body.to_string()).reasons, Vec::<&str>::new()); + assert_eq!(body["result"]["small"], 12, "a number a double holds is left as it is"); + } + + /// §8.2: a result verifies after any JSON reader and writer has carried it. + /// RFC 8785 writes a whole double past 2^53 by its shortest digits padded + /// with zeros (2^60 as 1152921504606847000), and that spelling, the + /// double's own, is read as what was sealed; no other inexact one is. + #[test] + fn a_large_whole_double_verifies_as_its_canonical_text_writes_it() { + let key = SigningKey::from_bytes(&[3; 32]); + let x = 2f64.powi(60); + let req = json!({ "qasm": "OPENQASM 3.0;", "observables": [{ "pauli": "Z0", "coeff": x }] }); + assert!(exact_request(&req).is_ok()); + let mut result = json!({ "method": "statevector", "accuracy": { "class": "exact" }, "x": x }); + receipt::seal_into(&key, &req, &mut result); + let body = json!({ "request": req, "result": result }); + // As this host writes it, and as RFC 8785 writes it. + assert_eq!(check_text(&body.to_string()).reasons, Vec::<&str>::new()); + let canonical = String::from_utf8(receipt::canonical(&body)).unwrap(); + assert!(canonical.contains("1152921504606847000"), "{canonical}"); + assert_eq!(check_text(&canonical).reasons, Vec::<&str>::new(), "its own canonical text"); + let parsed: Value = serde_json::from_str(&canonical).unwrap(); + assert_eq!(check(&parsed).reasons, Vec::<&str>::new(), "read by a reader that keeps it as an integer"); + // The exact digits are the double too; any other digits are not. + assert_eq!(check_text(&canonical.replace("1152921504606847000", "1152921504606846976")).reasons, Vec::<&str>::new()); + for other in ["1152921504606847001", "1152921504606846980", "1152921504606847000000"] { + let reasons = check_text(&canonical.replace("1152921504606847000", other)).reasons; + assert!(reasons.contains(&INEXACT_NUMBER), "{other}: {reasons:?}"); + } + } + + /// §8.6: a set's receipt seals its request and its head, each item's its + /// own; a verifier gives a set its reasons as it gives a job its own. + #[test] + fn a_set_is_judged_by_its_own_rules() { + let key = SigningKey::from_bytes(&[3; 32]); + let set = super::super::sets::run(&json!({ "programs": ["OPENQASM 3.0; qubit q; x q;", "OPENQASM 3.0; qubit q; h q;"], "shots": 8 }), 1 << 30, &key, json!({ "unmetered": "a test" })).unwrap(); + assert_eq!(check(&set).reasons, Vec::<&str>::new(), "a genuine set"); + assert_eq!(check_text(&set.to_string()).reasons, Vec::<&str>::new()); + assert_eq!(check_bytes(set.to_string().as_bytes()).reasons, Vec::<&str>::new()); + } + + /// Every number of a set's request, its sweep's values and its shared + /// fields among them, is screened as a run's is. + #[test] + fn a_set_request_s_every_number_is_screened() { + let key = SigningKey::from_bytes(&[3; 32]); + let refused = |req: Value| { + let e = super::super::sets::run(&req, 1 << 30, &key, json!({ "unmetered": "a test" })).err().unwrap_or_else(|| panic!("sealed {req}")); + assert!(e.contains("9007199254740991"), "{e}"); + }; + refused(json!({ "qasm": "OPENQASM 3.0; input float theta; qubit q; rx(theta) q;", "sweep": { "theta": [9007199254740993u64] }, "shots": 4 })); + refused(json!({ "programs": ["OPENQASM 3.0; qubit q; x q;"], "shots": 4, "note": 9007199254740993u64 })); + } } diff --git a/wai/wai-quantum/src/engine/provider.rs b/wai/wai-quantum/src/engine/provider.rs index c3918d30b..70d4f856e 100644 --- a/wai/wai-quantum/src/engine/provider.rs +++ b/wai/wai-quantum/src/engine/provider.rs @@ -15,14 +15,21 @@ //! //! # What the receipt proves, and what it does not //! -//! A provider's result is labelled `measured`. Its receipt proves that this -//! result, with these counts, is what the signer's host received for this -//! request from the provider it names, for the compiled program whose hash it -//! carries, under the calibration snapshot the provider named, and that the -//! host compiled that program exactly and checked it against the circuit by -//! simulation before sending it. It does not prove that the device ran that -//! program, or ran anything: the counts are the provider's word. Nothing -//! recomputes them; a second run on the same device gives other counts. +//! A processor's result is labelled `measured`, and a simulator's or an +//! emulator's `simulated`, by the provenance the provider states. The result +//! says where its counts come from (`counts_source`: shot records, the +//! provider's own counts, or counts rebuilt from probabilities) and how the +//! device was asked to run the program (`execution`: what the client set off, +//! what it left to the provider's defaults, and whether the gates were to run +//! as written), and it carries the compiled program's text beside its hash. +//! +//! Its receipt shows that the key it names signed this result, these counts +//! and these statements, for this request (wai/extensions/quantum-providers.md +//! §8.5). That key is the host's: what the receipt says the host received, sent +//! and checked is the host's own word, and the counts are the provider's. It +//! does not prove that the device ran that program, or ran anything. Nothing +//! recomputes the counts; a second run on the same device gives other counts. +//! [`check`] holds a sealed result to the rules a verifier checks. use super::catalog::{MAX_OUTCOMES, MAX_SHOTS}; use crate::qasm::{self, Program}; @@ -39,9 +46,488 @@ pub const COMPARE_MAX_QUBITS: u32 = 22; /// on outcomes the circuit never gives). const SUPPORT_FLOOR: f64 = 1e-12; -/// What the accuracy of a provider's result says. +/// What the accuracy of a provider's result says, sealed in receipts before +/// results stated their counts' source and how the device was asked to run the +/// program. It claimed no mitigation, which no client then turned off or +/// recorded; kept as it was, for those receipts. pub const MEASURED_NOTE: &str = "counts measured on the device the provider names, as it returned them, with no mitigation; not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them"; +/// What the accuracy class `measured` says: a processor's result. +pub const MEASURED_STATED_NOTE: &str = "counts from the processor the provider names, read as counts_source says (shot records, the provider's own counts, or counts rebuilt from probabilities); not reproducible by recomputation: the same job run again gives other counts, and no simulation stands in for them; what was set off at the provider, and what was left to its defaults, is in execution"; + +/// What the accuracy class `simulated` says: a provider's simulator's or +/// emulator's result, which no device measured. +pub const SIMULATED_NOTE: &str = "counts from the provider's simulator or emulator, as provider.provenance names it, not measured on any device: classical software's sample of the program, read as counts_source says; they show what that software gives, under whatever noise it models, not what a device would"; + +/// What a simulator that states its results are approximate adds. +pub const APPROXIMATE_NOTE: &str = "; the provider states this simulator is approximate, so its counts may depart from the program's exact distribution by more than the shots alone"; + +/// Where a result's counts come from (wai/extensions/quantum-providers.md +/// §8.3). Counts rebuilt from probabilities are derived, not shot records. A +/// later text may add a source, in a minor release, so a `match` outside this +/// crate takes a wildcard arm. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum CountsSource { + /// Tallied here from one record per shot, as the device returned them. + Shots, + /// The provider's own counts, as it returned them. + VendorCounts, + /// Rebuilt here from the probabilities the provider returned: each + /// `p·shots` rounded down, and the shots left given to the largest + /// remainders, ties to the lower outcome. + Probabilities, +} + +impl CountsSource { + /// Every source. A slice, so that one added later changes no type. + pub const ALL: &'static [CountsSource] = &[CountsSource::Shots, CountsSource::VendorCounts, CountsSource::Probabilities]; + + /// `shots`, `vendor_counts` or `probabilities`. + pub fn label(self) -> &'static str { + match self { + CountsSource::Shots => "shots", + CountsSource::VendorCounts => "vendor_counts", + CountsSource::Probabilities => "probabilities", + } + } + + pub fn from_label(s: &str) -> Option { + CountsSource::ALL.iter().copied().find(|c| c.label() == s) + } +} + +/// The five things a provider may do to a program between receiving it and +/// running it, each stated in a result's `execution` (§8.3). +pub const ASPECTS: [&str; 5] = ["mitigation", "twirling", "decoupling", "remapping", "optimisation"]; + +/// What a host knows of how a provider's job ran, beside the counts: where the +/// counts come from, how the device was asked to run the program +/// (`execution`, from [`client_execution`], [`provider_execution`] or +/// [`unstated_execution`]), and whether the provider states its simulator is +/// approximate. +#[derive(Clone, Debug, PartialEq)] +#[non_exhaustive] +pub struct Statement { + pub counts_source: CountsSource, + pub execution: Value, + pub approximate: bool, +} + +impl Statement { + pub fn new(counts_source: CountsSource, execution: Value) -> Statement { + Statement { counts_source, execution, approximate: false } + } + + /// The same, for a simulator the provider states is approximate. + pub fn approximate(mut self, approximate: bool) -> Statement { + self.approximate = approximate; + self + } +} + +/// `execution` as a client states it, from the request body it sends: `fields`, +/// the body's members; `settings`, each pointer of `pointers` with the value the +/// body holds there (a pointer the body does not hold is left out), and +/// `extra`, settings that are not members of the body (`verbatim_box`); the +/// five `aspects` in [`ASPECTS`]' order, each `off`, `on` or `default`; and +/// whether the request asks for its gates to run as written. +pub fn client_execution(body: &Value, pointers: &[&str], extra: &[(&str, Value)], aspects: [&str; 5], verbatim: bool, note: &str) -> Value { + let mut fields: Vec<&str> = body.as_object().map(|o| o.keys().map(String::as_str).collect()).unwrap_or_default(); + fields.sort_unstable(); + let mut settings = serde_json::Map::new(); + for p in pointers { + if let Some(v) = body.pointer(p) { + settings.insert((*p).to_string(), v.clone()); + } + } + for (k, v) in extra { + settings.insert((*k).to_string(), v.clone()); + } + let mut e = json!({ "stated_by": "client", "sent": { "fields": fields, "settings": settings }, "verbatim": verbatim, "note": note }); + for (k, v) in ASPECTS.iter().zip(aspects) { + e[*k] = json!(v); + } + e +} + +/// `execution` as a provider of the open protocol states it in its `result` +/// answer (§5.5): its five aspects, each `off`, `on`, `none` or `unknown` (a +/// step it cannot vouch for), its `verbatim` +/// and an optional note, marked as the provider's word. Anything else is +/// refused with why. +pub fn provider_execution(stated: &Value) -> Result { + let o = stated.as_object().ok_or("`execution` is an object")?; + let mut e = json!({ "stated_by": "provider" }); + for k in ASPECTS { + match o.get(k).and_then(Value::as_str) { + Some(v @ ("off" | "on" | "none" | "unknown")) => e[k] = json!(v), + _ => return Err(format!("`execution.{k}` is off, on, none or unknown")), + } + } + e["verbatim"] = json!(o.get("verbatim").and_then(Value::as_bool).ok_or("`execution.verbatim` is true or false")?); + match o.get("note") { + None | Some(Value::Null) => {} + Some(Value::String(n)) => e["note"] = json!(n), + Some(_) => return Err("`execution.note` is a sentence".into()), + } + if let Some(why) = execution_problem(&e) { + return Err(why); + } + Ok(e) +} + +/// `execution` when no one states it: a provider that says nothing, or a job +/// recorded before its client said what it sends. +pub fn unstated_execution() -> Value { + let mut e = json!({ "stated_by": "nobody", "verbatim": null, "note": "neither the client nor the provider states what was set at the provider" }); + for k in ASPECTS { + e[k] = json!("unknown"); + } + e +} + +/// What is wrong with an `execution` object's form (§8.3), or `None`. +pub fn execution_problem(e: &Value) -> Option { + let Some(o) = e.as_object() else { return Some("`execution` is an object".into()) }; + let by = o.get("stated_by").and_then(Value::as_str).unwrap_or(""); + let (values, verbatim_ok): (&[&str], bool) = match by { + "client" => (&["off", "on", "default"], o.get("verbatim").is_some_and(Value::is_boolean)), + "provider" => (&["off", "on", "none", "unknown"], o.get("verbatim").is_some_and(Value::is_boolean)), + "nobody" => (&["unknown"], o.get("verbatim") == Some(&Value::Null)), + _ => return Some("`stated_by` is client, provider or nobody".into()), + }; + for k in ASPECTS { + if !o.get(k).and_then(Value::as_str).is_some_and(|v| values.contains(&v)) { + return Some(format!("`{k}` is one of {values:?} when the {by} states it")); + } + } + if !verbatim_ok { + return Some("`verbatim` is true or false, or null when nobody states it".into()); + } + if o.get("verbatim") == Some(&Value::Bool(true)) && ["remapping", "optimisation"].iter().any(|k| !matches!(o[*k].as_str(), Some("off" | "none"))) { + return Some("a program run as written is neither remapped nor optimised".into()); + } + let sent = o.get("sent"); + match (by, sent) { + ("client", Some(s)) => { + let fields_ok = s.get("fields").and_then(Value::as_array).is_some_and(|f| { + let names: Vec<&str> = f.iter().filter_map(Value::as_str).collect(); + names.len() == f.len() && names.windows(2).all(|w| w[0] < w[1]) + }); + if !fields_ok || !s.get("settings").is_some_and(Value::is_object) || s.as_object().is_some_and(|m| m.len() != 2) { + return Some("`sent` holds `fields`, the request's members in order, and `settings`".into()); + } + // Each setting is a pointer into a member the request holds, or a + // name that is no pointer (a setting the body holds otherwise). + // `fields` is in increasing order: a setting is looked for, not scanned for. + let fields: Vec<&str> = s["fields"].as_array().into_iter().flatten().filter_map(Value::as_str).collect(); + let first_token = |k: &str| k[1..].split('/').next().unwrap_or("").replace("~1", "/").replace("~0", "~"); + if let Some(k) = s["settings"].as_object().into_iter().flatten().map(|(k, _)| k).find(|k| if k.starts_with('/') { fields.binary_search(&first_token(k).as_str()).is_err() } else { k.is_empty() }) { + return Some(format!("the setting {k:?} is neither a pointer into a member sent nor a name")); + } + let set = s["settings"].as_object().is_some_and(|m| !m.is_empty()); + if ASPECTS.iter().any(|k| matches!(o[*k].as_str(), Some("off" | "on"))) && !set { + return Some("a client that turns something off or on says by which setting".into()); + } + } + ("client", None) => return Some("a client states what it `sent`".into()), + (_, Some(_)) => return Some("only a client states what it `sent`".into()), + _ => {} + } + if o.get("note").is_some_and(|n| !n.is_string()) { + return Some("`note` is a sentence".into()); + } + let known = ["stated_by", "sent", "verbatim", "note"]; + if let Some(k) = o.keys().find(|k| !known.contains(&k.as_str()) && !ASPECTS.contains(&k.as_str())) { + return Some(format!("`{k}` is not a member of execution")); + } + None +} + +/// What each built-in kind records (§8.3's table), as JSON pointers and +/// values: the counts' sources it may give, and the `execution` it states, +/// less its note, on a processor (`silicon`) or not. `None` for a kind the +/// table does not name, whose rows are the provider's own word. +fn kind_record(kind: &str, silicon: bool, execution: &Value) -> Option<(&'static [CountsSource], Vec)> { + let row = |fields: &[&str], settings: Value, aspects: [&str; 5], verbatim: bool| { + let mut e = json!({ "stated_by": "client", "sent": { "fields": fields, "settings": settings }, "verbatim": verbatim }); + for (k, v) in ASPECTS.iter().zip(aspects) { + e[*k] = json!(v); + } + e + }; + use CountsSource::*; + Some(match kind { + "sampler-v2" => (&[Shots], vec![row( + &["backend", "params", "program_id"], + json!({ "/params/options/dynamical_decoupling/enable": false, "/params/options/twirling/enable_gates": false, "/params/options/twirling/enable_measure": false }), + ["default", "off", "off", "default", "default"], + false, + )]), + "prx-circuits" => (&[VendorCounts], vec![row(&["circuits", "shots"], json!({}), ["default"; 5], false)]), + "native-turns" => { + let base = json!({ "/input/gateset": "native", "/settings/error_mitigation/debiasing": false }); + let mut rows = vec![row(&["backend", "input", "settings", "shots", "type"], base.clone(), ["off", "default", "default", "default", "default"], false)]; + // A simulator under a device's noise model, by the name it was given. + if let (false, Some(model)) = (silicon, execution.pointer("/sent/settings/~1noise~1model").filter(|m| m.as_str().is_some_and(|s| !s.is_empty()))) { + let mut noisy = base; + noisy["/noise/model"] = model.clone(); + rows.push(row(&["backend", "input", "noise", "settings", "shots", "type"], noisy, ["off", "default", "default", "default", "default"], false)); + } + (&[Probabilities], rows) + } + "cloud-tasks" => { + let fields = ["action", "clientToken", "deviceArn", "outputS3Bucket", "outputS3KeyPrefix", "shots"]; + let r = if silicon { + row(&fields, json!({ "verbatim_box": true }), ["default", "default", "default", "off", "off"], true) + } else { + row(&fields, json!({}), ["default"; 5], false) + }; + (&[Shots, Probabilities], vec![r]) + } + _ => return None, + }) +} + +/// Whether `v` is a whole number (of any spelling a double reads as one) at +/// least `min`, however large (§8.4). +fn whole(v: &Value, min: f64) -> bool { + v.as_f64().is_some_and(|x| x.is_finite() && x.fract() == 0.0 && x >= min) +} + +/// Whether `v` is a count's value: a whole number from 1 to 2^53 − 1 (§8.4). +fn count_value(v: &Value) -> bool { + whole(v, 1.0) && v.as_f64().is_some_and(|x| x <= 9007199254740991.0) +} + +/// JSON equality, numbers by their value: a number's spelling is not sealed. +fn same(a: &Value, b: &Value) -> bool { + match (a, b) { + (Value::Number(x), Value::Number(y)) => x.as_f64() == y.as_f64(), + (Value::Array(x), Value::Array(y)) => x.len() == y.len() && x.iter().zip(y).all(|(p, q)| same(p, q)), + (Value::Object(x), Value::Object(y)) => x.len() == y.len() && x.iter().all(|(k, p)| y.get(k).is_some_and(|q| same(p, q))), + _ => a == b, + } +} + +/// Whether `listed` (exact) and `omitted` sum to `shots`, each a whole number +/// a double holds: exactly, past where a double's sum rounds. +fn sums_to(listed: u128, omitted: f64, shots: f64) -> bool { + const FAR: f64 = 170141183460469231731687303715884105728.0; // 2^127 + if omitted < FAR && shots < FAR { + return listed + omitted as u128 == shots as u128; + } + // Only one this far from `listed` can differ from the other by it, and the + // difference of two within a factor of two is exact. + omitted <= shots && shots - omitted < FAR && (shots - omitted) as u128 == listed +} + +/// Whether `compiled.qubit_names` is as §8.1 has it: required of a client that +/// sends a program under the device's own names and says what it sent; an +/// object from each index to its name, naming every index of both layouts. +fn names_ok(c: &Value, kind: Option<&str>, by: Option<&str>) -> bool { + let Some(names) = c.get("qubit_names") else { + return !(by == Some("client") && matches!(kind, Some("cloud-tasks" | "prx-circuits"))); + }; + let Some(m) = names.as_object() else { return false }; + let index = |k: &str| !k.is_empty() && k.bytes().all(|b| b.is_ascii_digit()) && (k == "0" || !k.starts_with('0')); + // Every qubit the text acts on, as `$` and its digits, is named too. + let in_text = c.get("qasm3").and_then(Value::as_str).is_none_or(|t| { + t.split('$').skip(1).all(|rest| { + let digits: String = rest.chars().take_while(char::is_ascii_digit).collect(); + digits.is_empty() || digits.parse::().is_ok_and(|q| m.contains_key(&q.to_string())) + }) + }); + in_text + && m.iter().all(|(k, v)| index(k) && v.as_str().is_some_and(|s| !s.is_empty())) + && ["layout", "final_layout"].iter().all(|k| c[*k].as_array().into_iter().flatten().all(|q| q.as_f64().is_some_and(|x| m.contains_key(&format!("{:.0}", x + 0.0))))) +} + +fn hex64(v: &Value) -> bool { + v.as_str().is_some_and(|s| s.len() == 64 && s.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))) +} + +/// A provider's provenance as §5.1 has it: `silicon`, or `simulator` or +/// `emulator`, alone or followed by `:` and what it is. The class its results +/// take: `measured` for silicon, `simulated` for the others. +fn class_of_provenance(p: &str) -> Option<&'static str> { + let kind = p.split_once(':').map_or(p, |(k, _)| k); + match (kind, p) { + (_, "silicon") => Some("measured"), + ("simulator" | "emulator", _) => Some("simulated"), + _ => None, + } +} + +/// A result's class is not one a provider's result takes, or not the one +/// its provenance gives, or `approximate` is given otherwise than `true` on a +/// simulated result; or a result of another method claims a provider's class. +pub const PROVIDER_CLASS: &str = "provider_class"; +/// The request names one provider and the result another. +pub const REQUEST_PROVIDER: &str = "request_provider"; +/// The request asks for other shots than the result gives. +pub const REQUEST_SHOTS: &str = "request_shots"; +/// The counts are not of their form, or do not sum to the shots. +pub const COUNTS: &str = "counts"; +/// A stated result lacks a member §8.1 requires of it, or has one of the wrong type. +pub const PROVIDER_MEMBERS: &str = "provider_members"; +/// `counts_source` is not one of its values, or not one the kind records. +pub const COUNTS_SOURCE: &str = "counts_source"; +/// `execution` is not of its form, or not what the kind records. +pub const EXECUTION: &str = "execution"; +/// The compiled program's text is not the text its hash names. +pub const COMPILED_PROGRAM: &str = "compiled_program"; +/// A run that was not free carries no confirmation, or a free one claims one. +pub const COST_CONFIRMATION: &str = "cost_confirmation"; + +/// What is wrong with a sealed result of `request` as a provider's result +/// (§8.4), and what it says of itself for a verifier to show. A result of +/// another method is held only to [`PROVIDER_CLASS`]: it must not claim a +/// provider's class. +/// +/// A result that carries `counts_source` or `execution` is of the stated +/// form, and held to every rule. One that carries neither is of the earlier +/// form, and is held to the rules every provider result has kept: the class +/// `measured`, the request's provider and shots, and its counts' sum. It +/// verifies, and what it says of itself says so; a verifier cannot read when +/// a receipt was sealed, and does not say (§8.5). +pub fn check(request: &Value, result: &Value) -> (Vec<&'static str>, Option) { + let class = result.pointer("/accuracy/class").and_then(Value::as_str); + if result.get("method").and_then(Value::as_str) != Some("provider") { + let bad = matches!(class, Some("measured" | "simulated")); + return (if bad { vec![PROVIDER_CLASS] } else { vec![] }, None); + } + let mut out = Vec::new(); + let stated = result.get("counts_source").is_some() || result.get("execution").is_some(); + let provenance = result.pointer("/provider/provenance").and_then(Value::as_str); + let kind = result.pointer("/provider/kind").and_then(Value::as_str); + let approximate = result.pointer("/accuracy/approximate"); + // The class. + let want = if stated { provenance.and_then(class_of_provenance) } else { Some("measured") }; + let approximate_ok = match approximate { + None => true, + Some(Value::Bool(true)) => stated && class == Some("simulated"), + Some(_) => false, + }; + if class.is_none() || class != want || !approximate_ok { + out.push(PROVIDER_CLASS); + } + // The request and the result, one job. + if result.pointer("/provider/name").and_then(Value::as_str).is_none_or(|n| request.get("provider").and_then(Value::as_str) != Some(n)) { + out.push(REQUEST_PROVIDER); + } + let default_shots = json!(1024); + let asked = request.get("shots").filter(|s| !s.is_null()).unwrap_or(&default_shots); + if result.get("shots").is_none_or(|s| !same(s, asked)) { + out.push(REQUEST_SHOTS); + } + // The counts: outcomes of 0 and 1, all of one length, each counted at + // least once, summing with the shots left out to the shots; every outcome + // listed, or the most the result lists. + let counts_ok = (|| { + let m = result.get("counts")?.as_object()?; + let width = m.keys().next().map_or(0, String::len); + if !m.iter().all(|(k, v)| k.len() == width && k.bytes().all(|b| b == b'0' || b == b'1') && count_value(v)) { + return None; + } + let listed: u128 = m.values().filter_map(Value::as_f64).map(|x| x as u128).sum(); + let omitted = result.get("omitted_shots").filter(|v| whole(v, 0.0))?.as_f64()?; + let distinct = result.get("distinct_outcomes").filter(|v| whole(v, 0.0))?.as_f64()?; + let n = m.len() as f64; + let listing_ok = if omitted == 0.0 { distinct == n } else { n == super::catalog::MAX_OUTCOMES as f64 && distinct > n }; + (whole(result.get("shots")?, 1.0) && sums_to(listed, omitted, result["shots"].as_f64()?) && listing_ok).then_some(()) + })() + .is_some(); + if !counts_ok { + out.push(COUNTS); + } + let read_as = provenance.and_then(class_of_provenance); + let mut said = json!({ + "form": if stated { "stated" } else { "earlier" }, + "class": class, + "read_as": read_as, + "provenance": provenance, + "kind": kind, + }); + if !stated { + if class == Some("measured") && read_as == Some("simulated") { + said["note"] = json!("of the earlier form, whose class is measured whatever ran: its provenance is a simulator's or an emulator's, so read it as simulated"); + } + said["note_unstated"] = json!("of the earlier form: it states neither where its counts come from nor what was set at the provider"); + return (out, Some(said)); + } + // The stated form: every member §8.1 requires. + let p = &result["provider"]; + let c = &result["compiled"]; + let members_ok = ["name", "kind", "protocol", "device"].iter().all(|k| p.get(*k).is_some_and(|v| v.as_str().is_some_and(|s| !s.is_empty()))) + && read_as.is_some() + && hex64(&p["describe"]) + && whole(&p["qubits"], 1.0) + && result.get("calibration").is_some_and(|v| v.is_null() || v.is_string()) + && c.get("basis").and_then(Value::as_str).is_some_and(|b| ["rzsxxcz", "prxcz", "gpizz"].contains(&b)) + && p.get("basis") == c.get("basis") + && hex64(&c["program"]) + && c.get("qasm3").is_some_and(Value::is_string) + && ["native_gates", "two_qubit_gates", "swaps"].iter().all(|k| whole(&c[*k], 0.0)) + && ["layout", "final_layout"].iter().all(|k| c.get(*k).and_then(Value::as_array).is_some_and(|a| a.iter().all(|q| whole(q, 0.0)))) + && c.get("checked").is_some_and(Value::is_string) + && names_ok(c, kind, result.pointer("/execution/stated_by").and_then(Value::as_str)) + && result.pointer("/job/id").and_then(Value::as_str).is_some_and(|s| !s.is_empty()) + && whole(&result["job"]["polls"], 0.0) + && result.pointer("/ideal/computed").is_some_and(Value::is_boolean) + && result.pointer("/cost/free").is_some_and(Value::is_boolean) + && result.pointer("/cost/confirmed").is_some_and(Value::is_boolean) + && result.pointer("/cost/estimate").is_some_and(Value::is_object) + && result.pointer("/cost/charged").is_some_and(|v| v.is_null() || (v["amount"].is_string() && v["currency"].is_string())) + && result.pointer("/energy/unmetered").is_some_and(Value::is_string) + && (result.pointer("/energy/service_kwh").is_none() || result.pointer("/energy/service_kwh_is").is_some_and(Value::is_string)) + && result.get("schema").and_then(Value::as_str) == Some(super::schema::JOB_RESULT) + && result.pointer("/engine/name").and_then(Value::as_str) == Some("wai-quantum") + && result.pointer("/engine/version").is_some_and(Value::is_string); + if !members_ok { + out.push(PROVIDER_MEMBERS); + } + let silicon = provenance == Some("silicon"); + let execution = result.get("execution").cloned().unwrap_or(Value::Null); + let record = kind.and_then(|k| kind_record(k, silicon, &execution)); + let source = result.get("counts_source").and_then(Value::as_str).and_then(CountsSource::from_label); + if source.is_none_or(|s| record.as_ref().is_some_and(|(allowed, _)| !allowed.contains(&s))) { + out.push(COUNTS_SOURCE); + } + let by = execution.get("stated_by").and_then(Value::as_str); + let mut bare = execution.clone(); + if let Some(o) = bare.as_object_mut() { + o.remove("note"); + } + let execution_ok = execution_problem(&execution).is_none() + && match (&record, kind) { + // A job recorded before its client said what it sent is no one's word. + (Some((_, rows)), _) => by == Some("nobody") || rows.iter().any(|row| same(row, &bare)), + // The open protocol's: the provider's word, or no one's. + (None, Some("process" | "http")) => matches!(by, Some("provider" | "nobody")), + (None, _) => true, + }; + if !execution_ok { + out.push(EXECUTION); + } + if let (Some(text), true) = (c.get("qasm3").and_then(Value::as_str), hex64(&c["program"])) + && blake3::hash(text.as_bytes()).to_hex().as_str() != c["program"].as_str().unwrap_or_default() + { + out.push(COMPILED_PROGRAM); + } + if let (Some(free), Some(confirmed)) = (result.pointer("/cost/free").and_then(Value::as_bool), result.pointer("/cost/confirmed").and_then(Value::as_bool)) + && confirmed == free + { + out.push(COST_CONFIRMATION); + } + // As the result gives it, a value of none of §8.3's included. + said["counts_source"] = result.get("counts_source").cloned().unwrap_or(Value::Null); + said["execution"] = execution; + said["approximate"] = json!(approximate == Some(&Value::Bool(true))); + (out, Some(said)) +} + /// A provider job, read and checked. pub struct Request { pub program: Program, @@ -129,12 +615,42 @@ pub struct Measured { pub energy: Value, } -/// The result of a provider job, in the service's form: the counts keyed by -/// the program's classical bits (the highest bit first), labelled -/// `measured`, with the circuit's exact distribution beside them when it is -/// at most [`COMPARE_MAX_QUBITS`] wide. +/// The result of a provider job as results were sealed before they stated +/// their counts' source and how the device was asked to run the program +/// (§8.3): labelled `measured`, whatever ran it. A host that knows those +/// states them with [`result_stated`], which every host here uses. pub fn result(r: &Request, m: Measured) -> Result { + result_of(r, m, None) +} + +/// The result of a provider job, in the service's form: the counts keyed by +/// the program's classical bits (the highest bit first), with the circuit's +/// exact distribution beside them when it is at most [`COMPARE_MAX_QUBITS`] +/// wide; labelled `measured` when the provider's provenance is `silicon` and +/// `simulated` when it is a simulator's or an emulator's (§8.1); and with +/// `counts_source` and `execution` as `stated` gives them. +pub fn result_stated(r: &Request, m: Measured, stated: &Statement) -> Result { + if let Some(why) = execution_problem(&stated.execution) { + return Err(format!("the job's execution is not of its form: {why}")); + } + result_of(r, m, Some(stated)) +} + +fn result_of(r: &Request, m: Measured, stated: Option<&Statement>) -> Result { let n = r.program.n_qubits; + let accuracy = match stated { + None => json!({ "class": "measured", "note": MEASURED_NOTE }), + Some(s) => { + let provenance = m.provider.get("provenance").and_then(Value::as_str).unwrap_or(""); + match class_of_provenance(provenance) { + Some("measured") if s.approximate => return Err("a processor's counts are not approximate; a simulator's may be".into()), + Some("measured") => json!({ "class": "measured", "note": MEASURED_STATED_NOTE }), + Some(_) if s.approximate => json!({ "class": "simulated", "approximate": true, "note": format!("{SIMULATED_NOTE}{APPROXIMATE_NOTE}") }), + Some(_) => json!({ "class": "simulated", "note": SIMULATED_NOTE }), + None => return Err(format!("the provider's provenance {provenance:?} is not silicon, a simulator or an emulator")), + } + } + }; let total: u64 = m.counts.iter().sum(); if m.counts.len() != 1usize << n || total != r.shots { return Err(format!("the provider's counts are {} outcomes summing to {total}, for {n} qubits and {} shots", m.counts.len(), r.shots)); @@ -145,12 +661,15 @@ pub fn result(r: &Request, m: Measured) -> Result { *map.entry(r.program.bitstring(|q| x >> q & 1 == 1)).or_insert(0) += k; } } - let ideal = compare(r, &map); + let mut ideal = compare(r, &map); + if stated.is_some() && ideal["computed"] == true { + ideal["note"] = json!(IDEAL_STATED_NOTE); + } let mut out = json!({ "method": "provider", "qubits": n, "gates": r.program.ops.len(), - "accuracy": { "class": "measured", "note": MEASURED_NOTE }, + "accuracy": accuracy, "shots": r.shots, "provider": m.provider, "calibration": m.calibration, @@ -160,6 +679,10 @@ pub fn result(r: &Request, m: Measured) -> Result { "cost": m.cost, "energy": m.energy, }); + if let Some(s) = stated { + out["counts_source"] = json!(s.counts_source.label()); + out["execution"] = s.execution.clone(); + } let mut v: Vec<(String, u64)> = map.into_iter().collect(); v.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(&b.0))); let distinct = v.len(); @@ -172,6 +695,10 @@ pub fn result(r: &Request, m: Measured) -> Result { Ok(out) } +/// What a stated result's `ideal` says: the provider's counts set beside the +/// exact distribution, whether a device or a simulator gave them. +pub const IDEAL_STATED_NOTE: &str = "the circuit's exact distribution over its classical bits, by the bit-exact state vector here, against the provider's counts: the total variation distance, the Hellinger fidelity, and the distance this many shots of the exact distribution would leave on average (by the normal approximation); a distance well above that is the device's or the simulator's error, or a program not run as sent"; + /// The measured counts against the circuit's exact distribution over the /// program's classical bits: the total variation distance, the Hellinger /// fidelity, and the distance the shots alone would leave on average. @@ -225,8 +752,7 @@ pub fn compare(r: &Request, measured: &BTreeMap) -> Value { /// in the service's form: `status`, `method` (`provider`), `why`, the /// request and the result with its receipt. [`super::job::verify`] checks it. pub fn seal(request: &Value, mut result: Value, key: &SigningKey, why: &str) -> Value { - let receipt = super::receipt::seal(key, request, &result); - result["receipt"] = receipt; + super::receipt::seal_into(key, request, &mut result); json!({ "status": "done", "method": "provider", "why": why, "request": request, "result": result }) } @@ -335,4 +861,299 @@ mod tests { assert_eq!(c["computed"], false); assert!(c["why"].as_str().unwrap().contains("at most 22"), "{c}"); } + + /// What a host knows of a run, as the built-in clients state it. + fn stated_measured(provenance: &str, kind: &str) -> Measured { + let mut m = measured(vec![498, 0, 0, 502]); + m.provider = json!({ "name": "lab", "kind": kind, "protocol": "wai.quantum.provider/1", "device": "lab:dev", "provenance": provenance, "qubits": 5, "basis": "rzsxxcz", "describe": "ab".repeat(32) }); + let text = "OPENQASM 3.0;\nbit[2] c;\ncz $0, $1;\n"; + m.compiled = json!({ "basis": "rzsxxcz", "program": blake3::hash(text.as_bytes()).to_hex().to_string(), "qasm3": text, "native_gates": 9, "two_qubit_gates": 1, "swaps": 0, "layout": [0, 1], "final_layout": [0, 1], "checked": "checked here" }); + m.cost = json!({ "free": true, "estimate": { "free": true }, "charged": null, "confirmed": false }); + m + } + + fn provider_says(e: Value) -> Value { + provider_execution(&e).unwrap() + } + + fn sealed_stated(provenance: &str, kind: &str, s: &Statement) -> Value { + let req = json!({ "qasm": BELL, "provider": "lab", "shots": 1000 }); + let r = read(&req).unwrap(); + let key = SigningKey::from_bytes(&[9; 32]); + seal(&req, result_stated(&r, stated_measured(provenance, kind), s).unwrap(), &key, "on lab") + } + + fn reasons(body: &Value) -> Vec<&'static str> { + let mut r = super::super::job::check(body).reasons; + r.sort_unstable(); + r + } + + /// Re-seals a body after `edit`, so that only the rule it breaks is found. + fn resealed(body: &Value, edit: impl Fn(&mut Value)) -> Value { + let mut req = body["request"].clone(); + let mut res = body["result"].clone(); + res.as_object_mut().unwrap().shift_remove("receipt"); + let mut both = json!({ "request": req, "result": res }); + edit(&mut both); + req = both["request"].take(); + res = both["result"].take(); + seal(&req, res, &SigningKey::from_bytes(&[9; 32]), "edited") + } + + #[test] + fn a_simulator_s_counts_are_simulated_and_a_processor_s_measured() { + let shots = Statement::new(CountsSource::Shots, provider_says(json!({ "mitigation": "none", "twirling": "none", "decoupling": "none", "remapping": "none", "optimisation": "none", "verbatim": true }))); + let sim = sealed_stated("simulator:a provider's state vector", "process", &shots); + assert_eq!((sim["result"]["accuracy"]["class"].as_str(), sim["result"]["counts_source"].as_str()), (Some("simulated"), Some("shots")), "{sim}"); + assert!(!sim["result"]["accuracy"]["note"].as_str().unwrap().contains("measured on the device")); + assert!(super::super::job::verify(&sim).unwrap().0, "{:?}", reasons(&sim)); + let emu = sealed_stated("emulator:a device's emulator", "process", &shots); + assert_eq!(emu["result"]["accuracy"]["class"], "simulated"); + let qpu = sealed_stated("silicon", "process", &shots); + assert_eq!(qpu["result"]["accuracy"]["class"], "measured"); + assert_eq!(reasons(&qpu), Vec::<&str>::new()); + // An approximate simulator says so; a processor never is. + let approx = sealed_stated("simulator:a truncated tensor network", "process", &shots.clone().approximate(true)); + assert_eq!((approx["result"]["accuracy"]["approximate"].as_bool(), approx["result"]["accuracy"]["class"].as_str()), (Some(true), Some("simulated"))); + assert_eq!(reasons(&approx), Vec::<&str>::new()); + let r = read(&json!({ "qasm": BELL, "provider": "lab", "shots": 1000 })).unwrap(); + assert!(result_stated(&r, stated_measured("silicon", "process"), &shots.clone().approximate(true)).unwrap_err().contains("not approximate")); + assert!(result_stated(&r, stated_measured("a guess", "process"), &shots).unwrap_err().contains("provenance")); + // A statement not of its form is not sealed. + assert!(result_stated(&r, stated_measured("silicon", "process"), &Statement::new(CountsSource::Shots, json!({ "stated_by": "client" }))).is_err()); + } + + #[test] + fn every_rule_a_verifier_checks_is_broken_once_and_found_alone() { + let e = provider_says(json!({ "mitigation": "none", "twirling": "none", "decoupling": "none", "remapping": "none", "optimisation": "none", "verbatim": true })); + let good = sealed_stated("simulator:a provider's state vector", "process", &Statement::new(CountsSource::Shots, e)); + assert_eq!(reasons(&good), Vec::<&str>::new()); + type Edit = Box; + let cases: Vec<(&str, Edit)> = vec![ + (PROVIDER_CLASS, Box::new(|b| b["result"]["accuracy"]["class"] = json!("measured"))), + (PROVIDER_CLASS, Box::new(|b| b["result"]["accuracy"]["approximate"] = json!(false))), + (REQUEST_PROVIDER, Box::new(|b| b["request"]["provider"] = json!("another"))), + (REQUEST_SHOTS, Box::new(|b| b["request"]["shots"] = json!(999))), + (COUNTS, Box::new(|b| b["result"]["counts"]["00"] = json!(497))), + (COUNTS, Box::new(|b| b["result"]["distinct_outcomes"] = json!(3))), + (COUNTS, Box::new(|b| b["result"]["omitted_shots"] = json!(1))), + (COUNTS, Box::new(|b| b["result"]["counts"] = json!({ "00": 498, "1": 502 }))), + (PROVIDER_MEMBERS, Box::new(|b| { b["result"].as_object_mut().unwrap().shift_remove("schema"); })), + (PROVIDER_MEMBERS, Box::new(|b| b["result"]["provider"]["describe"] = json!("AB".repeat(32)))), + (PROVIDER_MEMBERS, Box::new(|b| { b["result"]["compiled"].as_object_mut().unwrap().shift_remove("qasm3"); })), + (PROVIDER_MEMBERS, Box::new(|b| b["result"]["energy"] = json!({ "joules": 1 }))), + (COUNTS_SOURCE, Box::new(|b| b["result"]["counts_source"] = json!("guessed"))), + (EXECUTION, Box::new(|b| b["result"]["execution"]["twirling"] = json!("default"))), + (EXECUTION, Box::new(|b| b["result"]["execution"]["remapping"] = json!("on"))), + (EXECUTION, Box::new(|b| b["result"]["execution"]["stated_by"] = json!("client"))), + (COMPILED_PROGRAM, Box::new(|b| b["result"]["compiled"]["qasm3"] = json!("OPENQASM 3.0;\nbit[2] c;\ncz $0, $2;\n"))), + (COST_CONFIRMATION, Box::new(|b| b["result"]["cost"]["free"] = json!(false))), + (COST_CONFIRMATION, Box::new(|b| b["result"]["cost"]["confirmed"] = json!(true))), + ]; + for (want, edit) in cases { + let body = resealed(&good, edit); + assert_eq!(reasons(&body), vec![want], "{}", body["result"]); + } + // A kind of the table must state what it sends. + let as_kind = |kind: &str, b: &Value| resealed(b, |x| x["result"]["provider"]["kind"] = json!(kind)); + assert_eq!(reasons(&as_kind("prx-circuits", &good)), vec![COUNTS_SOURCE, EXECUTION]); + // A result of another method claims no provider's class. + let mut other = resealed(&good, |x| x["result"]["method"] = json!("statevector")); + assert_eq!(reasons(&other), vec![PROVIDER_CLASS]); + other = resealed(&other, |x| x["result"]["accuracy"]["class"] = json!("exact")); + assert_eq!(reasons(&other), Vec::<&str>::new()); + } + + #[test] + fn a_built_in_kind_s_row_is_what_its_client_sends() { + let processor = client_execution(&json!({ "action": "", "clientToken": "", "deviceArn": "", "outputS3Bucket": "", "outputS3KeyPrefix": "", "shots": 1 }), &[], &[("verbatim_box", json!(true))], ["default", "default", "default", "off", "off"], true, "a note"); + let mut bare = processor.clone(); + bare.as_object_mut().unwrap().remove("note"); + assert!(kind_record("cloud-tasks", true, &processor).unwrap().1.contains(&bare)); + assert!(!kind_record("cloud-tasks", false, &processor).unwrap().1.contains(&bare), "a simulator's task has no verbatim box"); + // A noise model is stated by its name, on a simulator only. + let noisy = client_execution(&json!({ "backend": "simulator", "input": { "gateset": "native" }, "noise": { "model": "a-device" }, "settings": { "error_mitigation": { "debiasing": false } }, "shots": 1, "type": "t" }), &["/input/gateset", "/settings/error_mitigation/debiasing", "/noise/model"], &[], ["off", "default", "default", "default", "default"], false, "n"); + let mut bare = noisy.clone(); + bare.as_object_mut().unwrap().remove("note"); + assert!(kind_record("native-turns", false, &noisy).unwrap().1.contains(&bare)); + assert!(!kind_record("native-turns", true, &noisy).unwrap().1.contains(&bare)); + assert!(kind_record("http", true, &noisy).is_none()); + // A provider may say it cannot vouch for a step; a client says what it sent. + assert!(provider_execution(&json!({ "mitigation": "unknown", "twirling": "none", "decoupling": "none", "remapping": "off", "optimisation": "off", "verbatim": true })).is_ok()); + assert!(provider_execution(&json!({ "mitigation": "default", "twirling": "none", "decoupling": "none", "remapping": "off", "optimisation": "off", "verbatim": true })).is_err()); + // A setting points into a member the request holds, or is a name. + let mut astray = noisy.clone(); + astray["sent"]["settings"]["/elsewhere/x"] = json!(1); + assert!(execution_problem(&astray).unwrap().contains("pointer")); + astray["sent"]["settings"] = json!({ "/settings/error_mitigation/debiasing": false, "verbatim_box": true }); + assert_eq!(execution_problem(&astray), None); + // What no one states is no one's. + assert_eq!(execution_problem(&unstated_execution()), None); + assert!(execution_problem(&json!("off")).is_some()); + } + + #[test] + fn a_result_sealed_before_it_stated_anything_verifies_and_says_so() { + let body: Value = serde_json::from_str(include_str!("testdata/provider-job-legacy.json")).unwrap(); + let c = super::super::job::check(&body); + assert!(c.valid(), "{:?}", c.reasons); + let p = c.provider.unwrap(); + assert_eq!((p["form"].as_str(), p["class"].as_str(), p["read_as"].as_str()), (Some("earlier"), Some("measured"), Some("simulated")), "{p}"); + assert!(p["note"].as_str().unwrap().contains("read it as simulated")); + } + + fn stated_body() -> Value { + let shots = Statement::new(CountsSource::Shots, provider_says(json!({ "mitigation": "none", "twirling": "none", "decoupling": "none", "remapping": "none", "optimisation": "none", "verbatim": true }))); + sealed_stated("silicon", "process", &shots) + } + + /// §8.4 caps only a count's value: any other whole number is whole however + /// large, as long as a double holds it. + #[test] + fn a_whole_number_has_no_ceiling_and_only_a_count_has_one() { + for (path, big) in [("/job/polls", json!(9007199254740992u64)), ("/job/polls", json!(1e16)), ("/provider/qubits", json!(1e30)), ("/compiled/native_gates", json!(9007199254740992u64)), ("/compiled/two_qubit_gates", json!(1e16)), ("/compiled/swaps", json!(1e30))] { + let body = resealed(&stated_body(), |b| *b["result"].pointer_mut(path).unwrap() = big.clone()); + assert_eq!(reasons(&body), Vec::<&str>::new(), "{path} {big}"); + } + let layout = resealed(&stated_body(), |b| b["result"]["compiled"]["layout"] = json!([9007199254740992u64])); + assert_eq!(reasons(&layout), Vec::<&str>::new()); + // The shots past 2^53 - 1, and two counts within it that sum to them. + let big = resealed(&stated_body(), |b| { + b["request"]["shots"] = json!(9007199254740992u64); + b["result"]["shots"] = json!(9007199254740992u64); + b["result"]["counts"] = json!({ "00": 9007199254740991u64, "11": 1 }); + }); + assert_eq!(reasons(&big), Vec::<&str>::new()); + // A count past it is not a count. + let count = resealed(&stated_body(), |b| { + b["request"]["shots"] = json!(9007199254740992u64); + b["result"]["shots"] = json!(9007199254740992u64); + b["result"]["counts"] = json!({ "00": 9007199254740992u64 }); + b["result"]["distinct_outcomes"] = json!(1); + }); + assert_eq!(reasons(&count), vec![COUNTS]); + } + + /// "Not the request's" is by what the two say, whatever they are: equal + /// strings are the same shots, and their not being numbers is another rule. + #[test] + fn shots_are_the_requests_when_the_two_are_the_same_value() { + let strings = resealed(&stated_body(), |b| { + b["request"]["shots"] = json!("300"); + b["result"]["shots"] = json!("300"); + }); + assert_eq!(reasons(&strings), vec![COUNTS]); + // A double's spelling is not sealed. + let spelled = resealed(&stated_body(), |b| b["result"]["shots"] = json!(1000.0)); + assert_eq!(reasons(&spelled), Vec::<&str>::new()); + } + + /// A count's sum is exact, past where a double's sum rounds. + #[test] + fn counts_sum_exactly_past_what_a_double_adds() { + // 2^53 - 1 + 2^53 - 1 + 2 is 2^54, and in doubles the sum rounds on the way. + let exact = resealed(&stated_body(), |b| { + b["request"]["shots"] = json!(18014398509481984u64); + b["result"]["shots"] = json!(18014398509481984u64); + b["result"]["counts"] = json!({ "00": 9007199254740991u64, "01": 9007199254740991u64, "11": 2 }); + b["result"]["distinct_outcomes"] = json!(3); + }); + assert_eq!(reasons(&exact), Vec::<&str>::new()); + // 1 + 2^53 - 1 + 2^53 - 1 is 2^54 - 1, which no double holds: never equal to 2^54. + let short = resealed(&stated_body(), |b| { + b["request"]["shots"] = json!(18014398509481984u64); + b["result"]["shots"] = json!(18014398509481984u64); + b["result"]["counts"] = json!({ "00": 9007199254740991u64, "01": 9007199254740991u64, "11": 1 }); + b["result"]["distinct_outcomes"] = json!(3); + }); + assert_eq!(reasons(&short), vec![COUNTS]); + } + + /// A verifier's time on `sent` is proportional to its size, with no key + /// needed to make it large. + #[test] + fn what_was_sent_is_checked_in_time_proportional_to_it() { + let n = 100_000; + let fields: Vec = (0..n).map(|i| format!("field-{i:07}")).collect(); + let settings: serde_json::Map = (0..n).map(|i| (format!("/field-{:07}/{i}", n - 1), json!(1))).collect(); + let e = json!({ "stated_by": "client", "mitigation": "default", "twirling": "default", "decoupling": "default", "remapping": "default", "optimisation": "default", "verbatim": false, "sent": { "fields": fields, "settings": settings } }); + let t = std::time::Instant::now(); + assert_eq!(execution_problem(&e), None); + assert!(t.elapsed() < std::time::Duration::from_secs(10), "{:?}", t.elapsed()); + } + + /// The earlier form is a form, chosen by what the result carries. A + /// verifier cannot read when a receipt was sealed, and does not say. + #[test] + fn the_earlier_form_is_named_and_no_time_is_claimed_for_it() { + let req = json!({ "qasm": BELL, "provider": "lab", "shots": 1000 }); + let r = read(&req).unwrap(); + let mut m = measured(vec![498, 0, 0, 502]); + m.provider["provenance"] = json!("simulator:a state vector"); + let body = seal(&req, result(&r, m).unwrap(), &SigningKey::from_bytes(&[9; 32]), "on lab"); + let said = super::super::job::check(&body).provider.unwrap(); + assert_eq!(said["form"], "earlier"); + for k in ["note", "note_unstated"] { + let text = said[k].as_str().unwrap_or_else(|| panic!("{said}")); + assert!(text.contains("earlier form") && !text.contains("sealed before") && !text.contains("sealed as measured before"), "{text}"); + } + } + + /// A client that sends a program under the device's own names for its + /// qubits says which: the text is on indices, and nothing else says what + /// they meant. + #[test] + fn a_client_that_names_its_qubits_seals_the_names() { + let row = kind_record("cloud-tasks", false, &json!({})).unwrap().1.remove(0); + let e = { + let mut e = row; + e["note"] = json!("a test"); + e + }; + let s = Statement::new(CountsSource::Shots, e); + let named = |names: Option| { + resealed(&sealed_stated("simulator:the service's", "cloud-tasks", &s), |b| { + b["result"]["compiled"]["layout"] = json!([0, 1]); + b["result"]["compiled"]["final_layout"] = json!([1, 0]); + match &names { + Some(n) => b["result"]["compiled"]["qubit_names"] = n.clone(), + None => { + b["result"]["compiled"].as_object_mut().unwrap().remove("qubit_names"); + } + } + }) + }; + assert_eq!(reasons(&named(Some(json!({ "0": "4", "1": "7" })))), Vec::<&str>::new()); + // -0 is 0, as a key is written. + let zero = resealed(&named(Some(json!({ "0": "4", "1": "7" }))), |b| b["result"]["compiled"]["layout"] = json!([-0.0, 1])); + assert_eq!(reasons(&zero), Vec::<&str>::new()); + for bad in [None, Some(json!({ "0": "4" })), Some(json!({ "0": "4", "1": "" })), Some(json!({ "0": "4", "1": 7 })), Some(json!(["4", "7"])), Some(json!({ "0": "4", "1": "7", "x": "9" })), Some(json!({ "0": "4", "1": "7", "01": "9" }))] { + assert_eq!(reasons(&named(bad.clone())), vec![PROVIDER_MEMBERS], "{bad:?}"); + } + // A job whose client did not say what it sent cannot say this either. + let nobody = Statement::new(CountsSource::VendorCounts, unstated_execution()); + assert_eq!(reasons(&sealed_stated("silicon", "prx-circuits", &nobody)), Vec::<&str>::new(), "stated by nobody, on a kind with rows"); + } + + /// `compiled.qubit_names` names every qubit the program acts on, as `$i` in + /// its text, not only those of its layouts: routing can pass through one. + #[test] + fn qubit_names_name_every_qubit_the_text_acts_on() { + let row = kind_record("cloud-tasks", false, &json!({})).unwrap().1.remove(0); + let s = Statement::new(CountsSource::Shots, row); + let text = "OPENQASM 3.0;\nbit[2] c;\ncz $0, $2;\ncz $2, $1;\nc[0] = measure $0;\nc[1] = measure $1;\n"; + let named = |names: Value| { + resealed(&sealed_stated("simulator:the service's", "cloud-tasks", &s), |b| { + let c = &mut b["result"]["compiled"]; + c["qasm3"] = json!(text); + c["program"] = json!(blake3::hash(text.as_bytes()).to_hex().to_string()); + c["layout"] = json!([0, 1]); + c["final_layout"] = json!([0, 1]); + c["qubit_names"] = names.clone(); + }) + }; + assert_eq!(reasons(&named(json!({ "0": "4", "1": "7", "2": "9" }))), Vec::<&str>::new()); + assert_eq!(reasons(&named(json!({ "0": "4", "1": "7" }))), vec![PROVIDER_MEMBERS], "$2 is acted on and named nowhere"); + } } diff --git a/wai/wai-quantum/src/engine/pulse.rs b/wai/wai-quantum/src/engine/pulse.rs index fa493aad8..2a5440c00 100644 --- a/wai/wai-quantum/src/engine/pulse.rs +++ b/wai/wai-quantum/src/engine/pulse.rs @@ -684,8 +684,7 @@ pub fn check(job: &Value) -> Result { /// job's receipt takes, so [`super::job::verify`] checks it. #[cfg(feature = "quantum_receipt")] pub fn seal(request: &Value, mut result: Value, key: &ed25519_dalek::SigningKey) -> Value { - let r = super::receipt::seal(key, request, &result); - result["receipt"] = r; + super::receipt::seal_into(key, request, &mut result); json!({ "request": request, "result": result }) } @@ -743,6 +742,8 @@ pub fn op_of_tool(name: &str) -> Option<&'static str> { /// answer names the engine and its schema (`wai.quantum.pulse/1`, /// [`super::schema::PULSE`]). pub fn call(op: &str, request: &Value) -> Result { + #[cfg(feature = "quantum_receipt")] + super::job::exact_request(request)?; let mut out = match op { "schedule" => schedule(request), #[cfg(feature = "quantum_receipt")] diff --git a/wai/wai-quantum/src/engine/qec.rs b/wai/wai-quantum/src/engine/qec.rs index 9188184f6..a16493874 100644 --- a/wai/wai-quantum/src/engine/qec.rs +++ b/wai/wai-quantum/src/engine/qec.rs @@ -595,8 +595,7 @@ pub fn threshold(request: &Value) -> Result { /// job's receipt takes, so [`super::job::verify`] checks it. #[cfg(feature = "quantum_receipt")] pub fn seal(request: &Value, mut result: Value, key: &ed25519_dalek::SigningKey) -> Value { - let r = super::receipt::seal(key, request, &result); - result["receipt"] = r; + super::receipt::seal_into(key, request, &mut result); json!({ "request": request, "result": result }) } @@ -677,6 +676,8 @@ pub fn op_of_tool(name: &str) -> Option<&'static str> { /// them: the answer names the engine and its schema (`wai.quantum.qec/1`, /// [`super::schema::QEC`]). pub fn call(op: &str, request: &Value) -> Result { + #[cfg(feature = "quantum_receipt")] + super::job::exact_request(request)?; let mut out = match op { "code" => code(request), "decode" => decode(request), diff --git a/wai/wai-quantum/src/engine/receipt.rs b/wai/wai-quantum/src/engine/receipt.rs index 8252fe76e..a699405c3 100644 --- a/wai/wai-quantum/src/engine/receipt.rs +++ b/wai/wai-quantum/src/engine/receipt.rs @@ -11,7 +11,7 @@ //! `1.0` into `1`, and other readers reorder keys. use serde_json::{Value, json}; -use ed25519_dalek::{Signer as _, SigningKey, Verifier as _, VerifyingKey}; +use ed25519_dalek::{Signer as _, SigningKey, VerifyingKey}; /// The first receipts signed serde_json's own text, which only a reader that /// keeps each number's spelling and each object's key order can reproduce. @@ -25,8 +25,13 @@ fn hex(b: &[u8]) -> String { b.iter().map(|x| format!("{x:02x}")).collect() } -fn unhex(s: &str) -> Option> { - s.len().is_multiple_of(2).then_some(())?; +/// Lower-case hex of exactly `n` bytes (wai/extensions/quantum-providers.md +/// §8.1): one spelling only, so the receipt, which nothing seals, has no +/// second form that also verifies. +fn unhex(s: &str, n: usize) -> Option> { + if s.len() != 2 * n || !s.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f')) { + return None; + } (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16).ok()).collect() } @@ -45,14 +50,102 @@ pub fn digest(request: &Value, result: &Value) -> [u8; 32] { *blake3::hash(&o).as_bytes() } -/// The digest the first receipts signed (see [`DOMAIN_V1`]). +/// The digest the first receipts signed (see [`DOMAIN_V1`]), over each +/// value's first-form text ([`first_text`]). fn digest_v1(request: &Value, result: &Value) -> [u8; 32] { let mut o = DOMAIN_V1.to_vec(); - put(&mut o, request.to_string().as_bytes()); - put(&mut o, result.to_string().as_bytes()); + put(&mut o, &first_text(request)); + put(&mut o, &first_text(result)); *blake3::hash(&o).as_bytes() } +/// The text the first receipts sealed (wai/extensions/quantum-providers.md +/// §8.2): the compact text their writer, serde_json 1.0 between 2026-10-17 +/// and 2026-10-20, wrote of a value, members in the order held. It is written +/// here by the text's rules rather than by the serde_json linked today, whose +/// writer may change: those receipts must verify as they were sealed. +pub fn first_text(v: &Value) -> Vec { + let mut o = Vec::new(); + write_first(v, &mut o); + o +} + +fn write_first(v: &Value, o: &mut Vec) { + match v { + Value::Number(n) => match (n.as_u64(), n.as_i64()) { + (Some(u), _) => o.extend_from_slice(u.to_string().as_bytes()), + (None, Some(i)) => o.extend_from_slice(i.to_string().as_bytes()), + _ => o.extend_from_slice(first_number(n.as_f64().unwrap_or(f64::NAN)).as_bytes()), + }, + Value::Array(a) => { + o.push(b'['); + for (i, x) in a.iter().enumerate() { + if i > 0 { + o.push(b','); + } + write_first(x, o); + } + o.push(b']'); + } + Value::Object(m) => { + o.push(b'{'); + for (i, (k, x)) in m.iter().enumerate() { + if i > 0 { + o.push(b','); + } + write_str(k, o); + o.push(b':'); + write_first(x, o); + } + o.push(b'}'); + } + other => write(other, o), + } +} + +/// A double as the first form's writer wrote it: `0.0` and `-0.0` for zero; +/// else the shortest digits that read back as it, laid out by where the +/// decimal point falls, with `.0` after a whole number, and the exponent +/// signed (`1e+16`, `1e-7`). +pub fn first_number(x: f64) -> String { + if !x.is_finite() { + return "null".into(); + } + if x == 0.0 { + return if x.is_sign_negative() { "-0.0" } else { "0.0" }.into(); + } + let (s, n) = shortest(x.abs()); + let digits: String = s.iter().map(|d| char::from(b'0' + d)).collect(); + let k = s.len() as i32; + let mut out = String::new(); + if x < 0.0 { + out.push('-'); + } + if k <= n && n <= 16 { + out.push_str(&digits); + out.extend(std::iter::repeat_n('0', (n - k) as usize)); + out.push_str(".0"); + } else if 0 < n && n <= 16 { + out.push_str(&digits[..n as usize]); + out.push('.'); + out.push_str(&digits[n as usize..]); + } else if -5 < n && n <= 0 { + out.push_str("0."); + out.extend(std::iter::repeat_n('0', (-n) as usize)); + out.push_str(&digits); + } else { + out.push_str(&digits[..1]); + if k > 1 { + out.push('.'); + out.push_str(&digits[1..]); + } + out.push('e'); + out.push(if n - 1 < 0 { '-' } else { '+' }); + out.push_str(&(n - 1).abs().to_string()); + } + out +} + /// The RFC 8785 canonical text of `v`. pub fn canonical(v: &Value) -> Vec { let mut o = Vec::new(); @@ -226,6 +319,30 @@ fn reads_as(s: &[u8], n: i32, x: f64) -> bool { format!("0.{digits}e{n}").parse::().is_ok_and(|y| y == x) } +/// `result` with every whole number no double holds written as the double a +/// receipt seals, and its receipt: what a host's result says is then what was +/// sealed, and its own verifier accepts it. A request cannot be changed so; a +/// host refuses one holding such a number where it reads it +/// ([`super::job::exact_request`]). +pub fn seal_into(key: &SigningKey, request: &Value, result: &mut Value) { + fn round(v: &mut Value) { + match v { + Value::Array(a) => a.iter_mut().for_each(round), + Value::Object(m) => m.values_mut().for_each(round), + Value::Number(_) if inexact_double(v).is_some() => { + if let Some(x) = v.as_f64() { + *v = json!(x); + } + } + _ => {} + } + } + debug_assert!(inexact_double(request).is_none(), "a host seals a request it should have refused"); + round(result); + let seal = seal(key, request, result); + result["receipt"] = seal; +} + /// The first number in `v` that a double cannot hold exactly (an integer past /// ±2^53), by its path. Such a number would be read differently by readers that /// hold every number as a double, so the receipt could not bind it. @@ -260,6 +377,49 @@ pub fn inexact_number(v: &Value) -> Option { walk(v, &mut path).then(|| path.trim_start_matches('.').to_string()) } +/// The first whole number in `v` that a double does not hold exactly, by its +/// path: the receipt seals the double, so such a number says more than was +/// sealed (wai/extensions/quantum-providers.md §8.2). Unlike +/// [`inexact_number`], 2^53 and other whole numbers a double holds pass. +pub fn inexact_double(v: &Value) -> Option { + // Exact, or the canonical text of the double it reads as (§8.2): RFC 8785 + // writes a whole double past 2^53 by its shortest digits and zeros. + fn exact(n: &serde_json::Number) -> bool { + match (n.as_u64(), n.as_i64()) { + (Some(u), _) => ((u as f64) as u64 == u && (u as f64) < 18446744073709551616.0) || es_number(u as f64) == u.to_string(), + (None, Some(i)) => ((i as f64) as i64 == i && (i as f64) >= -9223372036854775808.0) || es_number(i as f64) == i.to_string(), + _ => true, + } + } + fn walk(v: &Value, path: &mut String) -> bool { + match v { + Value::Number(n) => !exact(n), + Value::Array(a) => a.iter().enumerate().any(|(i, x)| { + let at = path.len(); + path.push_str(&format!("[{i}]")); + let hit = walk(x, path); + if !hit { + path.truncate(at); + } + hit + }), + Value::Object(m) => m.iter().any(|(k, x)| { + let at = path.len(); + path.push('.'); + path.push_str(k); + let hit = walk(x, path); + if !hit { + path.truncate(at); + } + hit + }), + _ => false, + } + } + let mut path = String::new(); + walk(v, &mut path).then(|| path.trim_start_matches('.').to_string()) +} + /// The receipt for `result` of `request`, sealed with `key`. pub fn seal(key: &SigningKey, request: &Value, result: &Value) -> Value { let d = digest(request, result); @@ -268,18 +428,92 @@ pub fn seal(key: &SigningKey, request: &Value, result: &Value) -> Value { } /// Whether `receipt` is a valid seal of `result` of `request` under the key -/// it names. +/// it names: [`check`] finds nothing wrong with it. pub fn verify(request: &Value, result: &Value, receipt: &Value) -> bool { - let field = |k: &str| receipt.get(k).and_then(Value::as_str).and_then(unhex); - let (Some(key), Some(sig)) = (field("key"), field("signature")) else { return false }; - let (Ok(key), Ok(sig)) = (<[u8; 32]>::try_from(key.as_slice()), <[u8; 64]>::try_from(sig.as_slice())) else { return false }; - let Ok(vk) = VerifyingKey::from_bytes(&key) else { return false }; - let d = match receipt.get("canon").and_then(Value::as_str) { - Some(CANON) => digest(request, result), - None => digest_v1(request, result), - Some(_) => return false, + check(request, result, receipt).is_empty() +} + +/// The receipt is not of the form §8.1 gives: not an object, `alg` other +/// than `ed25519`, `canon` present and other than `rfc8785`, or a `key`, +/// `digest` or `signature` that is not lower-case hex of its length. +pub const RECEIPT_FORM: &str = "receipt_form"; +/// The digest the receipt states is not the digest of this request and result. +pub const DIGEST_MISMATCH: &str = "digest_mismatch"; +/// The key is no Ed25519 key a signature can rest on: not a point's canonical +/// encoding, of small order, or outside the prime-order subgroup. +pub const BAD_KEY: &str = "bad_key"; +/// The signature does not hold over the digest the receipt states: `S` not +/// below the group order, `R` not a canonical encoding or of small order, or +/// the equation false. +pub const BAD_SIGNATURE: &str = "bad_signature"; + +/// The order of the prime-order subgroup, ℓ = 2^252 + 27742317777372353535851937790883648493, little-endian. +const ELL: [u8; 32] = [ + 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10, +]; + +/// Whether a little-endian 256-bit number is below `bound`. +fn below(x: &[u8; 32], bound: &[u8; 32]) -> bool { + for i in (0..32).rev() { + if x[i] != bound[i] { + return x[i] < bound[i]; + } + } + false +} + +/// Whether `b` encodes its y coordinate canonically: y, the low 255 bits, is +/// below p = 2^255 − 19 (RFC 8032 §5.1.3). Points with x = 0 and the sign bit +/// set are of small order, and refused as such. +fn canonical_y(b: &[u8; 32]) -> bool { + let mut y = *b; + y[31] &= 0x7f; + let mut p = [0xffu8; 32]; + p[0] = 0xed; + p[31] = 0x7f; + below(&y, &p) +} + +/// What is wrong with `receipt` as a seal of `result` of `request`, as the +/// reasons §8.4 names: nothing, for a valid seal. When the receipt is not of +/// its form, that is the only reason. Otherwise the digest is recomputed and +/// compared, the key judged, and, for a key a signature can rest on, the +/// signature checked over the digest the receipt states, so that a body changed +/// after it was sealed shows as `digest_mismatch` alone. +/// +/// The signature is Ed25519 as RFC 8032 §5.1.7 states it, strictly: `S` below ℓ +/// (checked here, whatever features the library was built with), `A` and `R` +/// canonical encodings of points not of small order, `A` in the prime-order +/// subgroup, and `[S]B = R + [k]A` with no cofactor. +pub fn check(request: &Value, result: &Value, receipt: &Value) -> Vec<&'static str> { + let field = |k: &str, n: usize| receipt.get(k).and_then(Value::as_str).and_then(|s| unhex(s, n)); + let form = match receipt.get("canon") { + None => Some(false), + Some(Value::String(c)) if c == CANON => Some(true), + Some(_) => None, }; - field("digest").is_some_and(|g| g == d) && vk.verify(&d, &ed25519_dalek::Signature::from_bytes(&sig)).is_ok() + let (Some(canonical), true, Some(key), Some(stated), Some(sig)) = (form, receipt.get("alg").and_then(Value::as_str) == Some("ed25519"), field("key", 32), field("digest", 32), field("signature", 64)) else { + return vec![RECEIPT_FORM]; + }; + let mut reasons = Vec::new(); + let d = if canonical { digest(request, result) } else { digest_v1(request, result) }; + if stated != d { + reasons.push(DIGEST_MISMATCH); + } + let key: [u8; 32] = key.try_into().expect("32 bytes"); + let vk = VerifyingKey::from_bytes(&key).ok().filter(|vk| canonical_y(&key) && !vk.is_weak() && vk.to_edwards().is_torsion_free()); + let Some(vk) = vk else { + reasons.push(BAD_KEY); + return reasons; + }; + let sig: [u8; 64] = sig.try_into().expect("64 bytes"); + let r: [u8; 32] = sig[..32].try_into().expect("32 bytes"); + let s: [u8; 32] = sig[32..].try_into().expect("32 bytes"); + if !below(&s, &ELL) || !canonical_y(&r) || vk.verify_strict(&stated, &ed25519_dalek::Signature::from_bytes(&sig)).is_err() { + reasons.push(BAD_SIGNATURE); + } + reasons } #[cfg(test)] @@ -351,6 +585,28 @@ mod tests { eprintln!("{n} numbers agree"); } + /// The first form's text is what its writer, serde_json 1.0 as it was + /// linked when those receipts were sealed, wrote; written here by the + /// text's rules, it must equal that writer's on every pattern of the + /// number file, and on values of every kind in their order. + #[test] + fn the_first_form_s_text_is_its_writer_s() { + let mut n = 0; + for line in include_str!("testdata/es-number-tostring.txt").lines() { + let (bits, _) = line.split_once(' ').unwrap(); + let x = f64::from_bits(u64::from_str_radix(bits, 16).unwrap()); + if let Some(num) = serde_json::Number::from_f64(x) { + let v = Value::Number(num); + assert_eq!(String::from_utf8(first_text(&v)).unwrap(), v.to_string(), "{bits}"); + n += 1; + } + } + assert!(n > 3000); + let v: Value = serde_json::from_str(r#"{"z":[1,-1,18446744073709551615,-9223372036854775808,18446744073709551616,-0,1e16,1e-7,0.00001,1.5],"a":"x\n\u0001\"\\é","m":{"b":null,"a":true}}"#).unwrap(); + assert_eq!(String::from_utf8(first_text(&v)).unwrap(), v.to_string()); + assert_eq!(first_number(1e16), "1e+16"); + } + #[test] fn the_canonical_text_sorts_keys_and_writes_numbers_and_strings_one_way() { let v = json!({ "b": 1.0, "a": [true, null, "x\n\u{1}\"\\é"], "\u{e000}": 0.000001, "\u{1f600}": -0.0, "c": 1e21 }); @@ -386,6 +642,64 @@ mod tests { assert!(!verify(&req, &res, &other)); } + /// The seal's form and its signature, as §8.1 and §8.4 state them: one + /// spelling of each member, a key a signature can rest on, and the strict + /// equation, whatever the library's defaults. + #[test] + fn a_seal_is_held_to_its_form_and_to_a_strict_signature() { + let s = Signer::from_seed([7; 32]); + let req = json!({ "qasm": "OPENQASM 3.0;", "shots": 10 }); + let res = json!({ "method": "statevector", "counts": { "0": 10 } }); + let good = s.seal(&req, &res); + assert_eq!(check(&req, &res, &good), Vec::<&str>::new()); + let with = |k: &str, v: Value| { + let mut r = good.clone(); + r[k] = v; + check(&req, &res, &r) + }; + assert_eq!(with("alg", json!("rsa-pss")), vec![RECEIPT_FORM]); + assert_eq!(with("canon", Value::Null), vec![RECEIPT_FORM]); + assert_eq!(with("key", json!(good["key"].as_str().unwrap().to_uppercase())), vec![RECEIPT_FORM]); + assert_eq!(with("digest", json!(format!("+{}", &good["digest"].as_str().unwrap()[1..]))), vec![RECEIPT_FORM]); + // A two-byte character where a hex digit belongs is refused, not a panic. + assert_eq!(with("key", json!(format!("a\u{e9}{}", &good["key"].as_str().unwrap()[3..]))), vec![RECEIPT_FORM]); + assert_eq!(check(&req, &res, &json!("not an object")), vec![RECEIPT_FORM]); + // A changed result: its digest, and nothing else, since the signature + // holds over the digest the receipt states. + assert_eq!(check(&req, &json!({ "method": "statevector", "counts": { "0": 9 } }), &good), vec![DIGEST_MISMATCH]); + // S + ℓ is another spelling of the same signature, and refused. + let sig = unhex(good["signature"].as_str().unwrap(), 64).unwrap(); + let mut s_plus = [0u8; 32]; + let mut carry = 0u16; + for i in 0..32 { + let v = u16::from(sig[32 + i]) + u16::from(ELL[i]) + carry; + s_plus[i] = v as u8; + carry = v >> 8; + } + let other = [&sig[..32], &s_plus[..]].concat(); + assert_eq!(with("signature", json!(hex(&other))), vec![BAD_SIGNATURE]); + // The identity as the key, and as R, with S = 0: the equation holds for + // any digest under a lenient check. Here the key is refused. + let identity = { let mut b = [0u8; 32]; b[0] = 1; b }; + let d = digest(&req, &json!({ "method": "statevector", "counts": { "0": 999 } })); + let forged = json!({ "alg": "ed25519", "canon": CANON, "key": hex(&identity), "digest": hex(&d), "signature": hex(&[identity, [0u8; 32]].concat()) }); + assert_eq!(check(&req, &json!({ "method": "statevector", "counts": { "0": 999 } }), &forged), vec![BAD_KEY]); + // A key encoded with y ≥ p is the same point spelt twice: refused. + let mut alias = [0xffu8; 32]; + alias[0] = 0xee; + alias[31] = 0x7f; + assert!(!canonical_y(&alias) && canonical_y(&identity)); + assert_eq!(with("key", json!(hex(&alias))), vec![BAD_KEY]); + // A point of order 8 added to a good key: no small order, but outside + // the prime-order subgroup. + let order8: [u8; 32] = unhex("c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a", 32).unwrap().try_into().unwrap(); + let t = VerifyingKey::from_bytes(&order8).unwrap(); + assert!(t.is_weak()); + let mixed = (s.0.verifying_key().to_edwards() + t.to_edwards()).compress().to_bytes(); + assert!(!VerifyingKey::from_bytes(&mixed).unwrap().is_weak()); + assert_eq!(with("key", json!(hex(&mixed))), vec![BAD_KEY]); + } + #[test] fn integers_a_double_cannot_hold_are_found_by_path() { assert_eq!(inexact_number(&json!({ "seed": 9007199254740991u64, "shots": 10 })), None); @@ -394,4 +708,29 @@ mod tests { assert_eq!(inexact_number(&json!({ "noise": { "readout": 1e300 } })), None); } + /// R the identity, and S made so that the equation holds, by the holder of + /// the key (the corpus's own vectors): a lax check takes this, and a strict + /// one refuses R's order, or its spelling. + #[test] + fn an_r_of_small_order_or_ill_spelled_is_refused_where_the_equation_holds() { + use ed25519_dalek::{Signature, Verifier}; + for (name, lax) in [("crafted-seal-r-small-order", true), ("crafted-seal-r-noncanonical", false)] { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(format!("../quantum-conformance/receipts/jobs/{name}.json")); + let body: Value = serde_json::from_slice(&std::fs::read(&path).unwrap()).unwrap(); + let mut result = body["result"].clone(); + let seal = result.as_object_mut().unwrap().shift_remove("receipt").unwrap(); + let bytes = |k: &str| -> Vec { + let s = seal[k].as_str().unwrap(); + (0..s.len() / 2).map(|i| u8::from_str_radix(&s[2 * i..2 * i + 2], 16).unwrap()).collect() + }; + let key = VerifyingKey::from_bytes(&bytes("key").try_into().unwrap()).unwrap(); + let signature = Signature::from_bytes(&bytes("signature").try_into().unwrap()); + if lax { + // The vector is worth having only if the equation holds. + assert!(key.verify(&bytes("digest"), &signature).is_ok(), "{name}: the equation does not hold, so this tests nothing"); + assert!(key.verify_strict(&bytes("digest"), &signature).is_err()); + } + assert_eq!(check(&body["request"], &result, &seal), vec![BAD_SIGNATURE], "{name}"); + } + } } diff --git a/wai/wai-quantum/src/engine/sets.rs b/wai/wai-quantum/src/engine/sets.rs index c665b34a1..969a67a10 100644 --- a/wai/wai-quantum/src/engine/sets.rs +++ b/wai/wai-quantum/src/engine/sets.rs @@ -253,7 +253,7 @@ fn hex(b: &[u8]) -> String { b.iter().map(|x| format!("{x:02x}")).collect() } -fn unhex32(s: &str) -> Option<[u8; 32]> { +pub(crate) fn unhex32(s: &str) -> Option<[u8; 32]> { if s.len() != 64 { return None; } @@ -298,8 +298,7 @@ pub fn seal(key: &SigningKey, request: &Value, items: &[Item], plans: &[catalog: for (it, o) in items.iter().zip(outcomes) { sealed.push(o.map(|mut r| { r["energy"] = json!({ "unmetered": ITEM_ENERGY }); - let s = receipt::seal(key, &it.request, &r); - r["receipt"] = s; + receipt::seal_into(key, &it.request, &mut r); r })); } @@ -346,7 +345,7 @@ pub fn seal(key: &SigningKey, request: &Value, items: &[Item], plans: &[catalog: }); // The head names the engine and its schema, inside what the receipt seals. super::schema::stamp_as(&mut result, super::schema::SET); - result["receipt"] = receipt::seal(key, request, &result); + receipt::seal_into(key, request, &mut result); result["items"] = Value::Array(list); result } @@ -354,6 +353,8 @@ pub fn seal(key: &SigningKey, request: &Value, items: &[Item], plans: &[catalog: /// A set end to end, here: expanded, planned within `job_bytes`, run, and /// sealed with `key`, in the service's job form. pub fn run(request: &Value, job_bytes: u64, key: &SigningKey, energy: Value) -> Result { + // Every number of the set's request, its sweep's values among them: it is sealed too. + super::job::exact_request(request)?; let items = expand(request)?; let plans = plan(&items, job_bytes)?; let pairs: Vec<(Value, catalog::Plan)> = items.iter().map(|i| i.request.clone()).zip(plans.iter().cloned()).collect(); @@ -385,11 +386,8 @@ pub fn verify_item(set_request: &Value, head: &Value, set_receipt: &Value, item: let request = &item["request"]; let outcome: Outcome = match (item.get("result"), item.get("error").and_then(Value::as_str)) { (Some(r), None) => { - let mut bare = r.clone(); - if let Some(o) = bare.as_object_mut() { - o.remove("receipt"); - } - if !r.get("receipt").is_some_and(|s| receipt::verify(request, &bare, s)) { + // An item is a job, held to every rule a job is. + if !super::job::check(&json!({ "request": request, "result": r })).valid() { return false; } Ok(r.clone()) @@ -408,6 +406,7 @@ pub fn verify(job: &Value) -> Value { let (request, result) = (&job["request"], &job["result"]); let set_receipt = &result["receipt"]; let head = head(result); + // The seal alone; the rules on its numbers, by its form, are §8.6's (`job::check`). let signed = receipt::verify(request, &head, set_receipt); let items = result["items"].as_array().cloned().unwrap_or_default(); let derived = match expand(request) { @@ -602,4 +601,27 @@ mod tests { forged["result"]["items"][1]["error"] = json!("nothing went wrong"); assert_eq!(verify(&forged)["valid"], false); } + + /// An item is a job, held to every rule a job is held to. + #[test] + fn an_item_is_held_to_the_rules_of_a_job() { + let set = json!({ "programs": [BELL], "shots": 16 }); + let body = run(&set, 1 << 30, &key(), json!({ "unmetered": "a test" })).unwrap(); + assert_eq!(verify(&body)["valid"], true); + // Its class changed and every seal made again, by the holder of the key. + let mut r = body["result"].clone(); + let mut item = r["items"][0]["result"].clone(); + item.as_object_mut().unwrap().shift_remove("receipt"); + item["accuracy"]["class"] = json!("measured"); + let seal = receipt::seal(&key(), &r["items"][0]["request"], &item); + item["receipt"] = seal; + r["items"][0]["result"] = item.clone(); + let leaves = [leaf(&r["items"][0]["request"], &Ok(item))]; + r["root"] = json!(hex(&merkle::merkle_root(&leaves))); + r.as_object_mut().unwrap().shift_remove("receipt"); + let top = receipt::seal(&key(), &body["request"], &head(&r)); + r["receipt"] = top; + let v = verify(&json!({ "request": body["request"], "result": r })); + assert_eq!((v["signed"].as_bool(), v["root"].as_bool(), v["items_sealed"].as_bool(), v["valid"].as_bool()), (Some(true), Some(true), Some(false), Some(false)), "{v}"); + } } diff --git a/wai/wai-quantum/src/engine/variational.rs b/wai/wai-quantum/src/engine/variational.rs index 20e626aff..f1640c543 100644 --- a/wai/wai-quantum/src/engine/variational.rs +++ b/wai/wai-quantum/src/engine/variational.rs @@ -1639,8 +1639,7 @@ pub fn summary(op: &str, r: &Value) -> String { /// `{"request", "result"}` with the result sealed by `key`, in the form every /// job's receipt takes, so [`super::job::verify`] checks it. pub fn seal(request: &Value, mut result: Value, key: &ed25519_dalek::SigningKey) -> Value { - let r = receipt::seal(key, request, &result); - result["receipt"] = r; + receipt::seal_into(key, request, &mut result); json!({ "request": request, "result": result }) } diff --git a/wai/wai-quantum/src/ijson.rs b/wai/wai-quantum/src/ijson.rs index 49e6c8230..8b71ebb26 100644 --- a/wai/wai-quantum/src/ijson.rs +++ b/wai/wai-quantum/src/ijson.rs @@ -50,6 +50,18 @@ impl Json { } } + /// Whether any number in this value, at any depth, has a literal for which + /// `f` holds. + #[cfg(all(feature = "quantum_engine", feature = "quantum_receipt"))] + pub(crate) fn any_number(&self, f: impl Fn(&str) -> bool + Copy) -> bool { + match self { + Json::Number(t) => f(t), + Json::Array(items) => items.iter().any(|v| v.any_number(f)), + Json::Object(members) => members.iter().any(|(_, v)| v.any_number(f)), + _ => false, + } + } + /// The value of an unsigned JSON integer that fits a `u64`: a number written /// with no sign, fraction or exponent. `-0`, `2.0` and `2e0` are not one. pub(crate) fn as_u64(&self) -> Option { @@ -109,8 +121,21 @@ impl fmt::Display for NotIJson { /// `raw` read as one I-JSON value, with whitespace allowed around it. pub(crate) fn parse(raw: &[u8]) -> Result { + parse_with(raw, false) +} + +/// [`parse`], with noncharacters taken as the characters they are: a job +/// body read for its receipt (wai/extensions/quantum-providers.md §8.2), +/// whose strings any program may have written and every reader carries +/// unchanged. Every other rule is [`parse`]'s. +#[cfg(all(feature = "quantum_engine", feature = "quantum_receipt"))] +pub(crate) fn parse_lenient(raw: &[u8]) -> Result { + parse_with(raw, true) +} + +fn parse_with(raw: &[u8], noncharacters: bool) -> Result { let text = std::str::from_utf8(raw).map_err(|e| NotIJson { rule: "utf-8", at: e.valid_up_to() })?; - let mut p = Parser { s: text.as_bytes(), text, i: 0 }; + let mut p = Parser { s: text.as_bytes(), text, i: 0, noncharacters }; p.ws(); let v = p.value(0)?; p.ws(); @@ -171,6 +196,8 @@ struct Parser<'a> { s: &'a [u8], text: &'a str, i: usize, + /// Whether a noncharacter is taken (a receipt's body) or refused (a section). + noncharacters: bool, } impl Parser<'_> { @@ -350,7 +377,7 @@ impl Parser<'_> { c } }; - if is_noncharacter(c) { + if is_noncharacter(c) && !self.noncharacters { return Err(NotIJson { rule: "noncharacter", at }); } out.push(c); diff --git a/wai/wai-quantum/views/manifest.json b/wai/wai-quantum/views/manifest.json index cd4f7f3e9..918130adb 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": 803675, - "script": 95767, + "page": 984188, + "script": 95807, "styles": 50804, - "engine_wasm": 192618, - "engine_wasm_base64": 256824, - "engine_js": 399830 + "engine_wasm": 244659, + "engine_wasm_base64": 326212, + "engine_js": 510915 }, "sha256": { - "page": "116d594d14c367e297e3832b608a74ad329503b1f997fdc1a8d7fbf60050dc05", - "script": "3b32f314a4c3d5e253cf70f0cdb53378f6843f82dd1451b9b2c01acdd28c8d9a", + "page": "d1b59cace26a83f56663cf5ef3bcfedd686c726962c23bf0dcec425614fac7f9", + "script": "6fbf441c1d2f095f0a5f14adaeb10740add48c4122d01a8f15e03b0f59045bde", "styles": "11ebcd4416c68e9eaf1156ca6f7c92b9a96744895ed13c377eba98fdd8dca9a6", - "engine_wasm": "95fbb6afbe9b4e0e7b1798414a82770e44677f6b8272dfe3de2ba7b3ba530442", - "engine_js": "374a802274404d8e541277c2144c166722d929148f0686c327b409d76453c96f" + "engine_wasm": "a63601412a204dd661261935d1e9f391a6673d6e719fd8439b930c2f3d1efd33", + "engine_js": "cb64aa3f957b3025405875ea29bb55cd61c61beb65f85027dec541a64ce5113c" }, "sources": { "wai/quantum-studio/build-views.mjs": "da5b161829a990b3d1bb4c1b3ec7c8f254ff7b5078476156e504ae322bc4f010", @@ -35,7 +35,7 @@ "wai/quantum-studio/locales/en/frame.json": "8ed953be6f391aad92f56da73e5a4f4188de9f1d46dc125a7c2a228c48e59e82", "wai/quantum-studio/locales/en/gallery.json": "5d0c2992e39ea0f82541e0491b46f4fd724085d95a988f8c15a1e3657d2eb149", "wai/quantum-studio/locales/en/generators.json": "85eb826cc6353cf12bc3fa45bbb8721210a311f145b9b46493c3e2bb44e7a3c2", - "wai/quantum-studio/locales/en/hardware.json": "6434ec52dc4fbfc1c0bb490f4557ef5425a9e7605e9b7d8c6f7139d5550e1fab", + "wai/quantum-studio/locales/en/hardware.json": "acc7c5e92256cb22ef14dae1e72fc74598068878b441fe23ed3fe1e04ef93990", "wai/quantum-studio/locales/en/novelty.json": "7d2d166476dd37fecaf893521c69ce7b5e779223391c31e2f3f3b6102aded85f", "wai/quantum-studio/locales/en/predict.json": "4b345acb68cdd1af6831417e92f0f914920bf8970c5028ba485402e8da6673bd", "wai/quantum-studio/locales/en/rewrite_view.json": "b889a177b2ad87e8bd5fa2ccf9d662b9ca8823461c313eb6da30ae1dafb3e0f3", @@ -67,6 +67,6 @@ "wai/wai-quantum-views/Cargo.lock": "9a43b475a75eaabee2bfd530bfdcbde63ad39ec9fd5b048ae26794d118c0a361", "wai/wai-quantum-views/Cargo.toml": "6192b41019ef8f54d723fc166280dd44b69b3cd05d50035783eec7ed412ed804", "wai/wai-quantum-views/build.sh": "c7cb7c91ba0a3ee39e1e4427c4b8d5be21d289eab3b036b7466347c774b67871", - "wai/wai-quantum-views/src/lib.rs": "a9a20da6399ff58efde84eb5116f8e064ca9110f33b6b99546899ae5971636e0" + "wai/wai-quantum-views/src/lib.rs": "d73e812cac044e12fc024802ce5d34f123b869d9f968fcb4b276d76eb0a6a885" } } diff --git a/wai/wai-quantum/views/wai-quantum-views.html b/wai/wai-quantum/views/wai-quantum-views.html index 88c325ee7..63226e998 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 d210905dc..4e47f0ed3 100644 --- a/wai/wai-web/src/quantum_engine.rs +++ b/wai/wai-web/src/quantum_engine.rs @@ -311,21 +311,12 @@ pub fn engine_repair(src: &str, options: &str) -> String { } } -/// Whether a job's receipt seals its request and its result, checked here: -/// `{"valid", "key", "signer"}` (the signer as a `did:key`), or `{"error"}`. +/// Whether a job's receipt seals its request and its result and every rule +/// holds, checked here on its text: `{"valid", "reasons", "key", "form", +/// "signer"}` (the signer as a `did:key`), or `{"error"}`. #[wasm_bindgen] pub fn engine_verify(job_json: &str) -> String { - let v: Value = match serde_json::from_str(job_json) { - Ok(v) => v, - Err(e) => return error_json(format!("not JSON: {e}")), - }; - match job::verify(&v) { - Ok((valid, key)) => { - let signer = key.as_str().and_then(job::did_of); - json!({ "valid": valid, "key": key, "signer": signer }).to_string() - } - Err(e) => error_json(e), - } + job::verdict_of_text(job_json).map(|v| v.to_string()).unwrap_or_else(error_json) } /// Whether an estimate's answer holds, checked here: its seal, every @@ -764,6 +755,18 @@ pub fn engine_course_hint(id: &str, src: &str, level: u32, locale: &str, memory_ mod tests { use super::*; + /// `engine_verify` reads a job's text as the provider protocol's section 8.2 + /// reads it, and holds every rule of section 8.4. + #[test] + fn engine_verify_reads_a_job_s_text_and_holds_every_rule() { + let jobs = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/receipts/jobs"); + let said = |name: &str| -> Value { serde_json::from_str(&engine_verify(&std::fs::read_to_string(jobs.join(name)).unwrap())).unwrap() }; + assert_eq!(said("cli-run-statevector.json")["valid"], true); + assert_eq!(said("crafted-read-duplicate.json")["reasons"], json!(["unreadable"])); + assert_eq!(said("crafted-number-inexact.json")["reasons"], json!(["inexact_number"])); + assert_eq!(said("crafted-provider-measured-simulator.json")["reasons"], json!(["provider_class"])); + } + /// A check every build answers with the same bytes: the noisy lesson's /// starter, its density matrix and its gaps in Chinese. pub(super) const COURSE_CHECK: (&str, &str) = ("noise", "zh-CN");