open-standards/wai/wai-quantum-cli/src/main.rs
Transaction Science 980fffc34d
Some checks failed
wai-quantum WebGPU member / member · DX12 and Vulkan (cbio-windows) (pull_request) Successful in 13m20s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 5s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 8s
WAI operation catalogue / does this change touch it (pull_request) Successful in 8s
wai-audit collector and player integration / does this change touch it (pull_request) Successful in 11s
WAI energy receipts / does this change touch it (pull_request) Successful in 6s
WAI conformance / does this change touch it (pull_request) Successful in 9s
WAI examples / WAI examples · does this change touch them (pull_request) Successful in 9s
wai-quantum contract / does this change touch it (pull_request) Successful in 7s
wai-quantum CUDA member / does this change touch it (pull_request) Successful in 5s
wai-quantum command line / does this change touch it (pull_request) Successful in 10s
wai-quantum dataflow member / does this change touch it (pull_request) Successful in 7s
wai-quantum docs, course and locales / does this change touch it (pull_request) Successful in 8s
wai-quantum editors / does this change touch it (pull_request) Successful in 5s
wai-quantum FPGA member / does this change touch it (pull_request) Successful in 5s
wai-quantum other formats / does this change touch it (pull_request) Successful in 9s
wai-quantum signal processor / does this change touch it (pull_request) Successful in 15s
wai-quantum resource estimation / does this change touch it (pull_request) Successful in 12s
wai-quantum HIP member / does this change touch it (pull_request) Successful in 8s
wai-quantum for JavaScript / does this change touch it (pull_request) Successful in 5s
wai-quantum Metal member / does this change touch it (pull_request) Successful in 9s
wai-quantum OpenQASM reader / does this change touch it (pull_request) Successful in 10s
wai-quantum OpenCL member / does this change touch it (pull_request) Successful in 6s
wai-quantum neural-processor member / does this change touch it (pull_request) Successful in 7s
wai-quantum npu-cores member / does this change touch it (pull_request) Successful in 11s
wai-quantum on every processor / emulated · aarch64-unknown-linux-gnu (pull_request) Successful in 1m2s
wai-quantum on every processor / emulated · armv7-unknown-linux-gnueabihf (pull_request) Successful in 2m1s
wai-quantum pulse and dynamics / does this change touch it (pull_request) Successful in 7s
wai-quantum for Python / does this change touch it (pull_request) Successful in 7s
wai-quantum error correction and mitigation / does this change touch it (pull_request) Successful in 7s
wai-quantum-qpu / does this change touch it (pull_request) Successful in 7s
wai-quantum extension points / does this change touch it (pull_request) Successful in 7s
wai-quantum on every processor / emulated · riscv64gc-unknown-linux-gnu (pull_request) Successful in 1m45s
wai-quantum service / does this change touch it (pull_request) Successful in 7s
wai-quantum matrix-extension member / does this change touch it (pull_request) Successful in 7s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 7s
wai-quantum tensor-engine member / does this change touch it (pull_request) Successful in 7s
wai-quantum portable tensor program / does this change touch it (pull_request) Successful in 7s
wai-quantum variational and algorithm library / does this change touch it (pull_request) Successful in 9s
wai-quantum CPU vector units / does this change touch it (pull_request) Successful in 9s
wai-quantum verdicts and repairs / does this change touch it (pull_request) Successful in 7s
wai-quantum views in agent hosts / does this change touch it (pull_request) Successful in 7s
wai-quantum WebGPU member / does this change touch it (pull_request) Successful in 9s
wai-sink-android native sink library / does this change touch it (pull_request) Successful in 7s
wai-sink-swift native sink package / does this change touch it (pull_request) Successful in 7s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 7s
WAI Studio / does this change touch it (pull_request) Successful in 9s
wai-web wasm32 / does this change touch it (pull_request) Successful in 8s
wai-quantum on every processor / emulated · powerpc64-unknown-linux-gnu (pull_request) Successful in 3m58s
wai-quantum on every processor / emulated · loongarch64-unknown-linux-gnu (pull_request) Successful in 4m52s
wai-quantum on every processor / emulated · s390x-unknown-linux-gnu (pull_request) Successful in 4m57s
wai-quantum on every processor / emulated · i686-unknown-linux-gnu (pull_request) Successful in 4m45s
wai-quantum on every processor / emulated · powerpc64le-unknown-linux-gnu (pull_request) Successful in 9m20s
WAI conformance / fast integer kernels, every x86_64 kernel set (AVX-512 silicon) (pull_request) Successful in 5m47s
wai-quantum on every processor / emulated · sparc64-unknown-linux-gnu (pull_request) Successful in 17m57s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 10m34s
wai-quantum HIP member / member · HIP (framework-128gb) (pull_request) Successful in 2m12s
wai-quantum OpenCL member / member · OpenCL GPU (framework-128gb) (pull_request) Successful in 59s
wai-quantum npu-cores member / member · npu-cores, neural processor (framework-128gb silicon) (pull_request) Successful in 2m9s
wai-quantum portable tensor program / tensor program · integrated GPU (framework-128gb) (pull_request) Successful in 23s
wai-quantum CUDA member / member · CUDA (thor) (pull_request) Successful in 2m6s
wai-quantum CPU vector units / member · vector units, avx2 and avx512 (framework-128gb silicon) (pull_request) Successful in 36s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (thor) (pull_request) Successful in 51s
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Successful in 1m34s
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Successful in 37s
wai-quantum service / service energy · linux packages (pull_request) Successful in 1m35s
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Successful in 30s
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Successful in 10m41s
wai-quantum WebGPU member / member · GPU, Vulkan (jetson-hub) (pull_request) Successful in 20m13s
wai-quantum WebGPU member / member · GPU, Vulkan (thor) (pull_request) Successful in 24m54s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m45s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 1m26s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Successful in 1m40s
wai-audit collector and player integration / collector · linux (pull_request) Successful in 1m8s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (pull_request) Successful in 2m31s
WAI conformance / faces from keypoints under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m30s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 3m17s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 1m6s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 20s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 59s
wai-quantum contract / the policy is linked, and the deprecations page reads (pull_request) Successful in 6s
wai-quantum CUDA member / member · CUDA absent (linux) (pull_request) Successful in 2m10s
WAI operation catalogue / the catalogue and every door · linux (pull_request) Successful in 8m22s
wai-quantum contract / API compatibility · linux (pull_request) Successful in 4m22s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (pull_request) Successful in 12s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 12m3s
wai-quantum editors / editors · linux (pull_request) Successful in 2m38s
wai-quantum command line / command line · linux (pull_request) Successful in 3m48s
wai-quantum FPGA member / member · FPGA simulation absent (linux) (pull_request) Successful in 21s
wai-quantum dataflow member / member · dataflow device, simulated (pci 1e52:b140) (pull_request) Successful in 4m18s
wai-quantum command line / command line · macos (pull_request) Successful in 1m33s
wai-quantum dataflow member / member · dataflow device absent (macos) (pull_request) Successful in 15s
wai-quantum dataflow member / member · dataflow device, simulated (pci 1e52:401e) (pull_request) Successful in 5m19s
wai-quantum other formats / the set under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m45s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 10m7s
wai-quantum editors / editors · macos (pull_request) Successful in 1m9s
wai-quantum docs, course and locales / the course and the command line's languages · linux (pull_request) Successful in 5m50s
wai-quantum FPGA member / member · FPGA datapath, simulated (macos) (pull_request) Successful in 1m1s
wai-quantum OpenCL member / member · OpenCL GPU (macos) (pull_request) Successful in 23s
wai-quantum resource estimation / estimation and calibration · linux, release (pull_request) Successful in 2m13s
wai-quantum other formats / readers, writers and the set · linux (pull_request) Successful in 4m4s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 10m25s
wai-quantum OpenQASM reader / corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m43s
wai-quantum OpenQASM reader / reader and corpus · linux (pull_request) Successful in 2m3s
wai-quantum OpenCL member / member · OpenCL absent (linux) (pull_request) Successful in 28s
wai-quantum Metal member / member · Metal absent (linux) (pull_request) Successful in 41s
wai-quantum neural-processor member / member · neural processor absent (linux) (pull_request) Successful in 49s
wai-quantum pulse and dynamics / the same bits · macos (pull_request) Successful in 1m4s
wai-quantum contract / schemas and the committed tool lists · linux (pull_request) Successful in 12m56s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m2s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs · linux, release (pull_request) Successful in 1m26s
wai-quantum for JavaScript / javascript · linux (pull_request) Successful in 3m47s
wai-quantum extension points / the registry under wasm32-wasip2 (wasmtime) (pull_request) Successful in 53s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 15m18s
wai-quantum matrix-extension member / member · matrix extension, emulated (aarch64 Linux) (pull_request) Failing after 23s
wai-quantum for Python / python · macos (pull_request) Successful in 2m1s
wai-quantum-qpu / clients against their contracts (linux) (pull_request) Successful in 1m42s
wai-quantum-qpu / credentials in the keychain (macos) (pull_request) Successful in 22s
wai-quantum Metal member / member · Metal (macos) (pull_request) Successful in 1m12s
wai-quantum for Python / python · linux (pull_request) Successful in 3m35s
wai-quantum neural-processor member / member · neural processor (macos) (pull_request) Successful in 4m28s
wai-quantum extension points / the registry and the example · linux (pull_request) Successful in 2m42s
wai-quantum matrix-extension member / member · matrix extension absent (macos) (pull_request) Successful in 30s
wai-quantum portable tensor program / tensor program · CPU, mutants, GPU builds (pull_request) Successful in 37s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 20m45s
wai-quantum CPU vector units / member · vector unit, rvv 1024-bit (riscv64, emulated) (pull_request) Successful in 10s
wai-quantum CPU vector units / member · vector unit, sve 128-bit (aarch64, emulated) (pull_request) Successful in 17s
wai-quantum portable tensor program / tensor program · Metal and the arm64 CPU (macos) (pull_request) Successful in 57s
wai-quantum CPU vector units / member · vector unit, rvv 128-bit (riscv64, emulated) (pull_request) Successful in 19s
wai-quantum CPU vector units / member · vector unit, sve 2048-bit (aarch64, emulated) (pull_request) Successful in 15s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 16m36s
wai-quantum CPU vector units / member · vector unit, avx2 (x86_64 silicon) (pull_request) Successful in 10s
wai-quantum CPU vector units / member · vector unit, sve 512-bit (aarch64, emulated) (pull_request) Successful in 10s
wai-quantum CPU vector units / member · vector unit absent (macos) (pull_request) Successful in 37s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux, release (pull_request) Successful in 1m44s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 3m49s
wai-quantum service / service · macos (pull_request) Successful in 3m44s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (pull_request) Successful in 1m46s
wai-quantum service / service · linux (pull_request) Successful in 5m55s
wai-quantum error correction and mitigation / codes, decoders, texts and mitigation · linux, release (pull_request) Successful in 6m48s
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (pull_request) Successful in 2m22s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 3m40s
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Successful in 2m31s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 5m26s
wai-quantum signal processor / processor and member · signal processor, emulated (pull_request) Successful in 12m45s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 1m8s
wai-quantum views in agent hosts / views · the page and its engine (pull_request) Successful in 3m26s
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Successful in 2m30s
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Successful in 3m25s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 3m0s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (pull_request) Failing after 21m6s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 5m37s
wai-quantum views in agent hosts / views · a scripted host (pull_request) Successful in 7m7s
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Successful in 8m38s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 7m25s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 10m49s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 12m16s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 38m37s
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Successful in 16m37s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 12m6s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 24m35s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Has been cancelled
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Has been cancelled
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Has been cancelled
wai-quantum pulse and dynamics / the same bits · linux-arm64 (pull_request) Has been cancelled
wai-quantum extension points / the registry and the example · linux-arm64 (pull_request) Has been cancelled
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Has been cancelled
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (pull_request) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Has been cancelled
wai-quantum: an optimisation stops and goes on to the same bytes
A long optimisation ran to its end in one process or not at all; one
killed partway lost every step. The engine's optimiser now runs from a
checkpoint (engine::variational::optimize_resumable, call_optimize): after
every step its caller has the run as it stands and may stop it there, and
the checkpoint, wai.quantum.optimize-checkpoint/1, holds the steps taken
with their links and the optimiser's whole state (the point, Adam's
moments and the powers of its betas, SPSA's stream, the best point and the
counts), every number as the bits of its f64. Gone on with from any step,
a run ends with the same bytes as one that never stopped, for each
optimiser; a checkpoint of another request, or one whose steps were
changed, is refused (J052).

wai-quantum optimize --checkpoint FILE keeps the state in FILE as it goes
(at most once a second, and when it stops; written whole or not at all),
goes on from FILE when it is there, and removes it when the run ends; a
run killed and started again with the same arguments ends with the same
bytes, receipt and all. --slice N takes at most N steps, then stops with
the state kept (exit 2).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-03 17:46:09 -05:00

1766 lines
97 KiB
Rust

//! `wai-quantum`: OpenQASM programs run on this machine as the quantum job
//! service runs them, or sent to the service; every result names its method
//! and its accuracy and carries a signed receipt.
//!
//! ```text
//! wai-quantum run bell.qasm --shots 1000 # here, planned as the service plans it
//! wai-quantum run prog.qasm -o "Z0 Z1" -a 00 --json
//! wai-quantum plan prog.qasm # the method and why, without running
//! wai-quantum lint prog.qasm # each problem with its line
//! wai-quantum draw prog.qasm # the circuit as text
//! wai-quantum draw prog.qasm --svg > c.svg # as an SVG image (or --latex, as TikZ; --describe, in words)
//! wai-quantum debug prog.qasm --line 9 # the state before line 9: amplitudes, Bloch vectors, entanglement
//! wai-quantum debug prog.qasm --branches # every branch at its measurements in the middle, with its weight
//! wai-quantum dap # the debugger, for editors, over stdio
//! wai-quantum critique prog.qasm # its measures and smells, each with its lines
//! wai-quantum equivalent a.qasm b.qasm # the same circuit? and how the engine knows
//! wai-quantum improve prog.qasm > better.qasm # its smells fixed, checked the same circuit
//! wai-quantum shortcuts prog.qasm # each exact classical route and its cost here
//! wai-quantum compile prog.qasm --epsilon 1e-6 # Clifford+T within ε, each pass checked, a signed certificate
//! wai-quantum run prog.qasm --compile 1e-6 # compiled first, then run bit-exact; the result says how far
//! wai-quantum estimate prog.qasm # what a fault-tolerant machine needs: qubits, time, factories, signed
//! wai-quantum run ansatz.qasm --param theta=0.3 -o Z0 # a program's parameters (input float, input angle) bound
//! wai-quantum gradient ansatz.qasm --param theta=0.3 --term "Z0 Z1" # the exact gradient, with its rounding bound
//! wai-quantum optimize ansatz.qasm --param theta=0.3 --term Z0 # gradient descent, Adam or SPSA, every step sealed
//! wai-quantum vqe --term "X0 X1" --term "Z0 Z1" --layers 2 # the ground energy, beside exact diagonalisation
//! wai-quantum qaoa --graph ring.json --layers 2 # Max-Cut by QAOA, beside the maximum cut
//! wai-quantum build qpe --counting 5 --phase 0.3 # a standard algorithm's program, checked against its reference
//! wai-quantum run prog.qasm --intent ref.qasm # run, and the engine's verdict: how far it checked the intent
//! wai-quantum run prog.qasm --intent dist.json # an exact distribution (or intents) the program must give
//! wai-quantum repair prog.qasm > fixed.qasm # the mechanical fixes, on the lines the diagnostics flag alone
//! wai-quantum run ghz.qasm --input n=40 # a generator, read at n = 40
//! wai-quantum instance qft.qasm --input n=5 # a generator written out as plain OpenQASM
//! wai-quantum convert prog.qasm --to qir # QIR 2.0 (or jeff, or OpenQASM 3), the conversion checked
//! wai-quantum run prog.ll --shots 1000 # every verb reads QIR (.ll) and jeff (.jeff); --from names one
//! wai-quantum discover new ghz.json # the workbench: an archive for a specification
//! wai-quantum discover search ghz.json # cheaper generators, each checked at every size
//! wai-quantum history blame prog.qasm # whose hand each line and gate is in
//! wai-quantum locks check prog.qasm theirs.qasm # the rules each proposed change breaks
//! wai-quantum novelty prog.qasm # its nearest known construction, and what it rediscovers
//! wai-quantum run prog.qasm --history prog.history.json # the receipt binds who wrote it
//! wai-quantum submit prog.qasm # on the job service, its status followed to the end
//! wai-quantum submit a.qasm --program b.qasm # a set: one result per program, one receipt binding them
//! wai-quantum submit p.qasm --sweep theta=0.1,0.2,0.3 # a sweep of an input, every point run as one set
//! wai-quantum submit prog.qasm --dry-run # the service's plan: method, memory, time, energy, bounds
//! wai-quantum task start job.json # the service as a scheduler's resource (`task --help`)
//! wai-quantum verify result.json # a result's receipt, or a lab receipt
//! wai-quantum key # the key this user signs with
//! wai-quantum version --json # this binary's version, the engine's and each schema's
//! wai-quantum methods # each method and its reach here
//! wai-quantum diagnostics # the reader's diagnostic codes, each with its outcome
//! wai-quantum mcp # these tools for an agent, over stdio
//! wai-quantum mcp config # its entry for an agent client's configuration
//! wai-quantum new my-circuit --template bell # a project: program, intent, test, CI, MCP entry, AGENTS.md
//! wai-quantum test my-circuit # every intent checked by the engine, the receipts saved
//! wai-quantum setup-agent # the wai-quantum block in AGENTS.md, written or refreshed
//! wai-quantum docs llms # llms.txt, from the live tool lists
//! wai-quantum learn # the circuits course, each exercise checked by the engine
//! wai-quantum learn check bell bell.qasm # a program checked against a lesson's exercise
//! wai-quantum learn --lang zh-CN # in Simplified Chinese (or as LANG says)
//! wai-quantum fmt prog.qasm # the program in the one layout editors format to
//! wai-quantum lsp # the language service, for editors, over stdio
//! wai-quantum eval run --tasks tasks.json --model http://127.0.0.1:8080/v1 # a model scored by the engine on a task set
//! wai-quantum lab … # the research tools
//! wai-quantum providers … # your own quantum providers; run --provider NAME runs on one
//! wai-quantum targets list | import | show # device targets; run --target NAME runs as the device would
//! wai-quantum run rabi.qasm --method pulse # a pulse program (OpenPulse) integrated, its error bounded, the same bits everywhere
//! wai-quantum pulse schedule rabi.qasm # its frames' timelines, plays, waveforms and gate calls
//! wai-quantum pulse check job.json # a pulse job's receipt, its program, and the run again, bit for bit
//! wai-quantum completions zsh # shell completion
//! ```
//!
//! `run`, `plan` and `mcp` use the job service's own engine
//! ([`wai_quantum::engine`]), so a job gives here the result it gives there.
//! `run --json` prints the job as the service returns it: the request, the
//! plan, and the result with its receipt, which `verify` checks. Every
//! `--json` output follows a committed schema (`wai_quantum::engine::schema`,
//! and `schemas/` here for the MCP server's own tools); what each version
//! promises is the policy in `wai/wai-quantum/VERSIONING.md`.
//!
//! A generator is a program with integer `input`s, `const`s and `for` loops
//! ([`wai_quantum::qasm::parse_with`]): one program for a whole family of
//! circuits. Every command reads one circuit of the family, at the values
//! given with `--input`, and `instance` prints that circuit.
// The MCP server's tool list is one `json!` value, deeper than the default limit.
#![recursion_limit = "256"]
mod compile;
mod converge;
mod dap;
mod debug;
mod estimate;
mod eval;
mod discover;
mod guide;
mod fmt;
mod history;
mod lab;
mod learn;
mod locale;
mod locks;
mod lsp;
mod mcp;
mod project;
#[cfg(feature = "providers")]
mod providers;
mod pulse;
mod qec;
mod repair;
mod rewrite;
mod study;
mod targets;
mod taste;
mod variational;
#[cfg(feature = "service")]
mod service;
#[cfg(feature = "service")]
mod task;
use clap::{Args, CommandFactory, FromArgMatches, Parser, Subcommand};
use serde_json::{Value, json};
use std::path::{Path, PathBuf};
use wai_quantum::engine::{catalog, critique, draw, equiv, job, shortcuts};
use wai_quantum_cli::keys;
#[derive(Parser)]
#[command(
name = "wai-quantum",
version,
about = "OpenQASM programs run here as the quantum job service runs them, or sent to it; every result names its method and accuracy and is signed",
propagate_version = true
)]
struct Cli {
/// 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
#[arg(long = "input", value_name = "NAME=VALUE", global = true, value_parser = input_arg)]
inputs: Vec<(String, i64)>,
/// The language of the words the command line writes itself, en or zh-CN (by default the one LANG names); results, codes and the engine's own messages stay in English
#[arg(long, global = true, value_name = "LOCALE")]
lang: Option<String>,
/// The format of every program the command reads: qasm, qir (LLVM text) or jeff; by default the file's extension says (.ll or .qir is QIR, .jeff is jeff), and OpenQASM otherwise
#[arg(long, global = true, value_name = "FORMAT", value_parser = format_arg)]
from: Option<wai_quantum::engine::formats::Format>,
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
/// Run a program here, planned as the job service plans it, and sign the result
Run {
#[command(flatten)]
job: Job,
#[command(flatten)]
out: Output,
#[command(flatten)]
key: KeyArgs,
#[cfg(feature = "providers")]
#[command(flatten)]
provider: providers::RunArgs,
},
/// Show the method a program would run on and why, without running it
Plan {
#[command(flatten)]
job: Job,
/// Print the plan as JSON
#[arg(long)]
json: bool,
},
/// Write a generator out at its `--input` values, as plain OpenQASM
Instance {
/// The program; `-` or nothing reads standard input
file: Option<PathBuf>,
},
/// Carry a program between formats (OpenQASM 3, QIR as LLVM text, jeff), every conversion checked: what is written is read back and held to the program by the equivalence ladder; notes and the check go to standard error (exit 1 refused, 2 the check found it different)
Convert {
/// The program; `-` or nothing reads standard input; its format is --from, else its extension, else what its bytes look like
file: Option<PathBuf>,
/// The format to write: qasm, qir or jeff
#[arg(long, value_name = "FORMAT", value_parser = format_arg)]
to: wai_quantum::engine::formats::Format,
/// QIR's profile, base or adaptive: by default the base profile when the program's measurements all come at the end, else the adaptive one
#[arg(long)]
profile: Option<String>,
/// Write the program to this file (jeff is binary: give a file, or --json for its base64)
#[arg(short = 'o', long, value_name = "FILE")]
output: Option<PathBuf>,
/// Print the conversion as JSON: the program (base64 for jeff), the program as OpenQASM, the notes, what was read and written, and the check
#[arg(long)]
json: bool,
/// Write it without the check
#[arg(long)]
no_check: bool,
},
/// Check a program and print each problem with its line
Lint {
/// The program; `-` or nothing reads standard input
file: Option<PathBuf>,
/// Print the outcome as JSON
#[arg(long)]
json: bool,
},
/// Draw a program's circuit: as text, or as an SVG image, LaTeX (plain TikZ) or words
Draw {
/// The program; `-` or nothing reads standard input
file: Option<PathBuf>,
/// Wrap at this many columns
#[arg(long, default_value_t = 100)]
width: usize,
/// The drawing as a standalone SVG image, the Studio's picture
#[arg(long, conflicts_with_all = ["latex", "describe"])]
svg: bool,
/// The drawing as LaTeX: one picture in plain TikZ, which needs only \usepackage{tikz}
#[arg(long, conflicts_with = "describe")]
latex: bool,
/// The circuit in words, column by column, as a screen reader reads it
#[arg(long)]
describe: bool,
},
/// Step through a program on the engine's exact methods and look into the state at a position or before a line, or list every branch at its measurements in the middle with its weight
Debug(debug::Args),
/// Serve the debugger to editors over standard input and output (the debug adapter protocol): launch a program, breakpoints by line, step forward and back, and the state's probes and branch tree as variables
Dap,
/// A program's measures (gates, two-qubit gates, depth, T count, a modelled fidelity) and its smells, each with its lines
Critique {
/// The program; `-` or nothing reads standard input
file: Option<PathBuf>,
/// Print the critique as JSON
#[arg(long)]
json: bool,
},
/// Whether two programs are the same circuit, up to a global phase, and how the engine knows (exit 0 the same, 1 different, 2 unchecked)
Equivalent {
/// The first program
a: PathBuf,
/// The second program
b: PathBuf,
/// Print the verdict as JSON
#[arg(long)]
json: bool,
},
/// Print the program with its smells fixed, checked the same circuit; what changed goes to standard error
Improve {
/// The program; `-` or nothing reads standard input
file: Option<PathBuf>,
/// Print every candidate (all the fixes, and each alone) with its measures and verdict, as JSON
#[arg(long)]
json: bool,
},
/// Each exact classical route to simulating a program, with its cost on it, and the cheapest for each question
Shortcuts {
/// The program; `-` or nothing reads standard input
file: Option<PathBuf>,
/// Also search a contraction tree for one amplitude, and search longer for a cut-rank order (seconds to minutes on a large circuit)
#[arg(long)]
thorough: bool,
/// Print the routes as JSON
#[arg(long)]
json: bool,
},
/// Compile a program: passes each checked where they act, Clifford+T within an ε budget (or the standard gates), and a signed certificate of every pass and the distance (`wai-quantum compile --help`)
Compile(compile::Args),
/// What a fault-tolerant machine would need to run a program: its logical counts (T, Toffolis, rotations at their synthesised T counts, depth, qubits with ancillas), physical qubits, runtime and factories, and the frontier between qubits and time, each estimate signed (`wai-quantum estimate --help`)
Estimate(estimate::Args),
/// The expectation of a Hamiltonian at a program's parameters and its exact gradient by each (adjoint differentiation or the parameter-shift rule, in f64 with a stated rounding bound), signed
Gradient(variational::Gradient),
/// Minimise a Hamiltonian's expectation over a program's parameters: gradient descent, Adam or SPSA from a seed, every step in a digest chain the receipt seals; with --checkpoint it stops and goes on to the same bytes (exit 2 when --slice stops it)
Optimize(variational::Optimize),
/// The variational eigensolver: an ansatz optimised on a Hamiltonian, the energy found set beside the exact ground energy by diagonalisation
Vqe(variational::Vqe),
/// Max-Cut on a graph by QAOA: the program and its cost built from the graph, optimised, and the cut set beside the maximum cut by brute force
Qaoa(variational::Qaoa),
/// A standard algorithm's program (qpe, amplitude_estimation, adder, qsvt) on standard output, checked by the equivalence ladder against a reference built from its definition
Build(variational::Build),
/// Repair a program from its diagnostics, on the lines they flag alone: the mechanical fixes, a proposed fix checked against those lines, and what needs a person (exit 0 it reads, 1 something needs a person, 2 a proposal rejected)
Repair(repair::Args),
/// Send a program, a set or a sweep to the job service, follow its status to the end, and print its result
#[cfg(feature = "service")]
Submit {
#[command(flatten)]
job: Job,
#[command(flatten)]
out: Output,
/// The service's address
#[arg(long, env = "WAI_QUANTUM_SERVICE", default_value = "https://wai.transaction.science/quantum-jobs")]
service: String,
/// Ask the service for its plan, running nothing: the method, memory, estimated time and energy with their basis, the bounds, and whether it would take the job now (exit 1 if it would not)
#[arg(long)]
dry_run: bool,
/// Submit and print the job with its id, without following it (`wai-quantum task status ID` reads it later)
#[arg(long, conflicts_with = "dry_run")]
detach: bool,
/// A private deployment's API key (sent as a bearer token); the public service takes none
#[arg(long, env = "WAI_QUANTUM_API_KEY", hide_env_values = true)]
api_key: Option<String>,
},
/// The job service as a resource an on-premises scheduler hands its batch jobs: describe it, acquire and release a lease, start, follow, read and stop tasks (`wai-quantum task --help`)
#[cfg(feature = "service")]
Task {
#[command(subcommand)]
cmd: task::Cmd,
#[command(flatten)]
args: task::Args,
},
/// Check a receipt: a result from `run --json`, `submit --json` or the service, or a `lab` receipt
Verify {
/// The JSON; `-` or nothing reads standard input
file: Option<PathBuf>,
/// Also check that the job carries this history's account of its program (`run --history`)
#[arg(long, value_name = "FILE")]
history: Option<PathBuf>,
},
/// Show the key this user signs receipts with
Key {
#[command(flatten)]
key: KeyArgs,
},
/// This binary's version and the engine's, the schema of each document it writes, the protocols it speaks, and where the policy is that says what each version promises
Version {
/// Print them as JSON
#[arg(long)]
json: bool,
},
/// Each method, what it takes, and how far it reaches in a job's memory here
Methods {
/// Print the table as JSON
#[arg(long)]
json: bool,
/// The memory a job may take, GiB
#[arg(long, default_value_t = DEFAULT_MEMORY_GB)]
memory_gb: u64,
},
/// The members of the fabric a statevector job can be placed on here (`run --fabric`): the CPU and each accelerator this build reaches that is found and certified bit-exact against the CPU, each with the most qubits it holds; the devices found and not placed, and why
Fabric {
/// Print the list as JSON
#[arg(long)]
json: bool,
/// The memory a job may take, GiB
#[arg(long, default_value_t = DEFAULT_MEMORY_GB)]
memory_gb: u64,
},
/// Every component the engine plans, runs, compiles and converts through (the simulation methods, compiler passes, noise channels and program formats), each with its version and the digest of its descriptor: the identity a result is sealed with
Components {
/// Print the list as JSON, each component with its descriptor
#[arg(long)]
json: bool,
},
/// The diagnostic codes: the OpenQASM reader's (each with its outcome: refused, lowered, carried, preserved or warned) and the job layer's (refused or retry), each with its meaning
Diagnostics {
/// Print the table as JSON
#[arg(long)]
json: bool,
},
/// Serve run, plan, lint, critique, the equivalence check, improve, the classical shortcuts, compile, draw, the debugger and verify to an agent over standard input and output (MCP), with resources and prompts; `mcp config` and `mcp install` register it with a client
Mcp {
#[command(subcommand)]
cmd: Option<guide::McpCmd>,
/// The memory a job may take, GiB
#[arg(long, default_value_t = DEFAULT_MEMORY_GB)]
memory_gb: u64,
#[command(flatten)]
key: KeyArgs,
},
/// Format programs in one layout: a statement to a line, blocks indented two spaces, a space around each operator, comments and blank lines kept; a program whose brackets do not match is left as it is, with its line
Fmt {
/// The programs; `-` or nothing reads standard input and prints the formatted program
files: Vec<PathBuf>,
/// Change nothing: name each program that is not formatted, and exit 1 if any is not
#[arg(long, conflicts_with = "write")]
check: bool,
/// Write each file back formatted
#[arg(long)]
write: bool,
},
/// Serve the language to editors over standard input and output (the language server protocol): the engine's diagnostics, completion, hover with each gate's matrix and cost, definitions, references, renames, symbols, folding and formatting, and Run, Plan, Critique, Shortcuts and Receipt run here
Lsp {
/// The memory a job may take, GiB
#[arg(long, default_value_t = DEFAULT_MEMORY_GB)]
memory_gb: u64,
#[command(flatten)]
key: KeyArgs,
/// Accepted for the editors that pass it: standard input and output is the server's only transport
#[arg(long)]
stdio: bool,
},
/// The workbench: a specification, its archive, and searches for cheaper generators (`wai-quantum discover --help`)
Discover {
#[command(subcommand)]
cmd: discover::Cmd,
},
/// A program's history, attributed: its versions, who made each change and how, and whose hand each line and gate is in (`wai-quantum history --help`)
History {
#[command(subcommand)]
cmd: history::Cmd,
},
/// What is new in a program: its structural distance to the known constructions (and to programs given), each known construction the engine proves it to be, and the strengths a cost did not ask for
Novelty {
/// The program; `-` or nothing reads standard input
file: Option<PathBuf>,
/// More of the inspiring set: programs to compare with (repeatable)
#[arg(long = "against", value_name = "FILE")]
against: Vec<PathBuf>,
/// The program it is meant to improve on: the measures it beats it on that --weigh does not name
#[arg(long, value_name = "FILE")]
reference: Option<PathBuf>,
/// The measures the cost weighs, comma-separated (as depth,two_qubit)
#[arg(long, value_delimiter = ',')]
weigh: Vec<String>,
#[arg(long)]
json: bool,
},
/// The person's rules for what a proposal may change: held blocks, gates, qubits, what to avoid, a region (`wai-quantum locks --help`)
Locks {
#[command(subcommand)]
cmd: locks::Cmd,
},
/// Measuring whether the co-design works: a protocol sealed before any data, conditions by key, records, and the cohort report (`wai-quantum study --help`)
Study {
#[command(subcommand)]
cmd: study::Cmd,
},
/// Whether your designs converge on a model's proposals, and how diverse a set of programs is (`wai-quantum converge --help`)
Converge {
#[command(subcommand)]
cmd: converge::Cmd,
},
/// Rewrites that keep a program's meaning, each checked: the ones it admits, one made, all to a fixed point, or how one program becomes another (`wai-quantum rewrite --help`)
Rewrite {
#[command(subcommand)]
cmd: rewrite::Cmd,
},
/// Your taste in circuits, learned from your own choices between programs the engine proves equal (`wai-quantum taste --help`)
Taste {
#[command(subcommand)]
cmd: taste::Cmd,
},
/// Error correction: a code and its certified distance, a syndrome or a circuit's detectors decoded, a threshold measured, each answer signed (`wai-quantum qec --help`)
Qec {
#[command(subcommand)]
cmd: qec::Cmd,
},
/// Pulse programs: a program's schedule on frames drawn as a timeline, and a pulse job checked by running it again; a pulse program runs with `run --method pulse` (`wai-quantum pulse --help`)
Pulse {
#[command(subcommand)]
cmd: pulse::Cmd,
},
/// Score a model on a task set with the engine: whether its program reads, the verdict's level and whether the intent holds, and the joules per solved task where its server's energy is metered; or write each task's answer three ways for a token count (`wai-quantum eval --help`)
Eval {
#[command(subcommand)]
cmd: eval::Cmd,
},
/// The research tools: error correction, DMRG, neural states, resource estimates and more (`wai-quantum lab help`)
Lab {
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
},
/// Make a project: a starter program, what it must do (intent.json), a test that checks it with the engine and saves the receipts, CI, an MCP entry and AGENTS.md
New(project::New),
/// Check every intent in a project's intent.json with the engine, run the program, and save the signed receipts (exit 0 every intent holds, 1 one fails, 2 one is unchecked)
Test(project::Test),
/// Write or refresh the wai-quantum block in a project's AGENTS.md (between its markers, nothing outside them), for coding agents
SetupAgent(guide::SetupAgent),
/// What agents are told, offline: the language, the diagnostics, the methods, the examples, the AGENTS.md block, llms.txt and the Agent Skill
Docs(guide::Docs),
/// The circuits course: lessons from a first circuit to resource estimates, each exercise checked by the engine, your progress kept only on this machine (`wai-quantum learn --help`)
Learn(learn::Args),
/// Device targets (a device's qubits, pairs, gates, durations and errors from its calibration snapshot): import, list, show; `run --target NAME` runs as the device would (`wai-quantum targets --help`)
Targets {
#[command(subcommand)]
cmd: targets::Cmd,
},
/// Your own quantum providers (devices, accounts, simulators): add, list, remove, describe and test them, and what is free (`wai-quantum providers --help`)
#[cfg(feature = "providers")]
Providers {
#[command(subcommand)]
cmd: providers::Cmd,
},
/// Print shell completion for bash, zsh, fish, elvish or PowerShell
Completions { shell: clap_complete::Shell },
}
/// The memory a job may take by default, GiB.
const DEFAULT_MEMORY_GB: u64 = 16;
/// A job: a program and what is asked of it, as the service reads it.
#[derive(Args, Clone)]
struct Job {
/// The OpenQASM 3 (or 2) program; `-` or nothing reads standard input
file: Option<PathBuf>,
/// Shots to sample (1024 when nothing else is asked)
#[arg(long)]
shots: Option<u64>,
/// The seed shots are sampled with (1 if not given)
#[arg(long)]
seed: Option<u64>,
/// A method by name, or auto (the default) for the strongest the program admits; `wai-quantum methods` lists them
#[arg(long)]
method: Option<String>,
/// A Pauli observable to take the expectation of, as "Z0 Z1" or "0.5 X2" (repeatable)
#[arg(short = 'o', long = "observable")]
observables: Vec<String>,
/// A basis state's amplitude, as a bit string with the highest qubit first (repeatable)
#[arg(short = 'a', long = "amplitude")]
amplitudes: Vec<String>,
/// A noise channel and its probability, as depolarizing_1q=0.01 (repeatable)
#[arg(long, value_parser = noise_arg)]
noise: Vec<(String, f64)>,
/// A method option, as max_bond=128 (repeatable)
#[arg(long = "option", value_parser = option_arg)]
options: Vec<(String, Value)>,
/// The memory a job may take here, GiB
#[arg(long, default_value_t = DEFAULT_MEMORY_GB)]
memory_gb: u64,
/// The program's history (`wai-quantum history`): the request carries its account of the program (the version's digest and whose hand its gates and lines are in), and the receipt binds it
#[arg(long, value_name = "FILE")]
history: Option<PathBuf>,
/// Compile the program to Clifford+T within this ε first (`wai-quantum compile`): it then runs bit-exact, and the result states the distance
#[arg(long, value_name = "EPSILON")]
compile: Option<f64>,
/// Run as the program would run on a device target (`wai-quantum targets list`, or a snapshot's file): compiled to its gates, placed where it is most likely to succeed, and simulated under its noise, its hash in the receipt
#[arg(long, value_name = "NAME|FILE", conflicts_with = "noise")]
target: Option<String>,
/// What the program must do, checked by the engine with the run (repeatable): a reference program (.qasm, the same circuit), or JSON: a distribution of the measured bits ({"00": 0.5, "11": 0.5}), an intent ({"prepares": "ref.qasm"}), a list of them, or a project's intent.json. The result carries the verdict, sealed by its receipt
#[arg(long, value_name = "FILE")]
intent: Vec<PathBuf>,
/// Ask for the verdict with no intent: how far the engine checked (parsed, executed), sealed by the receipt
#[arg(long)]
verdict: bool,
/// Repair the program first, on the lines its diagnostics flag alone (`wai-quantum repair`): the result says what was changed, and the verdict lists each repair
#[arg(long)]
repair: bool,
/// With --repair: the bound for an unbounded `while` on measured bits
#[arg(long, value_name = "N", requires = "repair")]
max_iterations: Option<u64>,
/// Another program for the same set (repeatable): the job runs as a set, one result and receipt per program and one receipt binding them all, the other options shared
#[arg(long = "program", value_name = "FILE", conflicts_with_all = ["sweep", "history"])]
programs: Vec<PathBuf>,
/// An input of the program and the values to run it at, as theta=0.1,0.2 or n=2,3 (repeatable): every combination runs as one set; an integer input takes whole numbers, a float or angle input any number
#[arg(long, value_name = "NAME=V1,V2,…", value_parser = sweep_arg, conflicts_with = "history")]
sweep: Vec<(String, Vec<Value>)>,
/// Mitigate the expectation values (or, for readout, the counts) under the job's noise: zne, pea, pec, cdr, vd or readout, or its options as JSON ({"method": "pea", "factors": [1, 1.5, 2]})
#[arg(long, value_name = "METHOD|JSON")]
mitigate: Option<String>,
/// A pulse run's options (with --method pulse), as JSON or a file of it: dt_ns, levels, tolerance, substeps, qubits, couplings and ports (`wai-quantum pulse --help`)
#[arg(long, value_name = "JSON|FILE", value_parser = pulse::options_arg)]
pulse: Option<Value>,
/// A parameter's value, as theta=0.3 (repeatable): each `input float` and `input angle` of the program, bound exactly (the job's "params"); in a sweep, the parameters it fixes
#[arg(long = "param", value_name = "NAME=VALUE", value_parser = variational::param_arg)]
params: Vec<(String, f64)>,
/// Where the state vector runs (the statevector method): auto (the default: on a member only where the committed measurements show it faster than this host's CPU for the job's size and fusion, otherwise the CPU; `wai-quantum fabric` states the rule and the numbers), cpu, accelerator (the first certified accelerator member that holds the state), or a member's id or key (`wai-quantum fabric` lists them); for a pauli job, accelerator or a member's id or key runs its sum of terms on that member, with the CPU's answers; repeated, the state is split by its top qubits into one partition on each member named, a power of two of them. A member certified bit-exact gives the CPU's answers; a placement this host cannot honour is refused (J013)
#[arg(long = "fabric", value_name = "PLACEMENT")]
fabric: Vec<String>,
}
/// `name=v1,v2,…`: each value a whole number if it reads as one, else a number.
fn sweep_arg(s: &str) -> Result<(String, Vec<Value>), String> {
let (k, v) = s.split_once('=').ok_or("a sweep is NAME=V1,V2,…")?;
let values = v
.split(',')
.map(|x| {
let x = x.trim();
x.parse::<i64>().map(|i| json!(i)).or_else(|_| x.parse::<f64>().ok().filter(|f| f.is_finite()).map(|f| json!(f)).ok_or(format!("{x}: not a number")))
})
.collect::<Result<Vec<_>, _>>()?;
if k.is_empty() || values.is_empty() {
return Err("a sweep is NAME=V1,V2,…".into());
}
Ok((k.to_string(), values))
}
#[derive(Args, Clone)]
struct Output {
/// Print the job as the service returns it: request, plan, result and receipt
#[arg(long)]
json: bool,
/// The most outcomes to list
#[arg(long, default_value_t = 16)]
top: usize,
}
#[derive(Args, Clone)]
struct KeyArgs {
/// Sign with the key in this file (64 hex digits)
#[arg(long, value_name = "FILE")]
key: Option<PathBuf>,
/// Sign with the published demo key: the receipt then shows a result is unaltered, not who signed it
#[arg(long, conflicts_with = "key")]
demo_key: bool,
}
fn noise_arg(s: &str) -> Result<(String, f64), String> {
let (k, v) = s.split_once('=').ok_or("give a channel and its probability, as depolarizing_1q=0.01")?;
if !catalog::NOISE_CHANNELS.contains(&k) {
return Err(format!("{k}: no such channel; the channels are {}", catalog::NOISE_CHANNELS.join(", ")));
}
let p: f64 = v.parse().map_err(|_| format!("{v}: a probability, 0 to 1"))?;
Ok((k.to_string(), p))
}
fn input_arg(s: &str) -> Result<(String, i64), String> {
let (k, v) = s.split_once('=').ok_or("give an input and its value, as n=5")?;
let v: i64 = v.trim().parse().map_err(|_| format!("{v}: an input takes a whole number"))?;
Ok((k.trim().to_string(), v))
}
/// The values given with `--input`, for every program the command reads.
static INPUTS: std::sync::OnceLock<wai_quantum::qasm::Inputs> = std::sync::OnceLock::new();
fn option_arg(s: &str) -> Result<(String, Value), String> {
let (k, v) = s.split_once('=').ok_or("give an option and its value, as max_bond=128")?;
// A number or true/false reads as JSON; anything else is a string.
let value = serde_json::from_str::<Value>(v).ok().filter(|x| x.is_number() || x.is_boolean()).unwrap_or_else(|| json!(v));
Ok((k.to_string(), value))
}
/// The components list, one line each: its kind, name, version, where it
/// comes from and its digest.
fn components_text(c: &Value) -> String {
let mut s = format!("engine {} {}\n", c["engine"]["name"].as_str().unwrap_or(""), c["engine"]["version"].as_str().unwrap_or(""));
for (kind, title) in [("methods", "methods"), ("passes", "passes"), ("channels", "noise channels"), ("front_ends", "formats")] {
s.push_str(&format!("\n{title}\n"));
for x in c[kind].as_array().into_iter().flatten() {
s.push_str(&format!(" {:<20} {:<10} {:<10} {}\n", x["name"].as_str().unwrap_or(""), x["version"].as_str().unwrap_or(""), x["origin"].as_str().unwrap_or(""), x["digest"].as_str().unwrap_or("")));
}
}
s
}
/// The fabric's members as `wai-quantum fabric` prints them.
fn fabric_text(f: &Value) -> String {
let s = |v: &Value| v.as_str().unwrap_or("").to_string();
let mut o = format!("kernel {}\nauto {}\n", s(&f["kernel"]), s(&f["auto"]));
let rule = &f["auto_rule"];
let places = rule["places"].as_array().cloned().unwrap_or_default();
for p in &places {
o.push_str(&format!("auto places {} from {} to {} qubits at fusion {}, for a plan of at least {} launches\n", s(&p["key"]), p["from_qubits"], p["to_qubits"], p["fusion"], p["min_launches"]));
}
if places.is_empty() {
let why = if rule["measured_here"].as_bool() == Some(true) { "no member found here is measured faster than this host's CPU" } else { "this host is not among the measured ones" };
o.push_str(&format!("auto places nothing here: {why}; every state on the CPU\n"));
}
for h in rule["hosts"].as_array().into_iter().flatten() {
let rows = h["rows"].as_array().cloned().unwrap_or_default();
o.push_str(&format!("measured {}, {} ({}), {} rows, {} faster; discovery {} ms once per process\n", s(&h["host"]), s(&h["measured"]), s(&h["about"]), rows.len(), rows.iter().filter(|r| r["faster"] == true).count(), h["discovery_ms"]));
}
for m in f["members"].as_array().into_iter().flatten() {
let corpus = m["corpus"].as_str().map(|c| format!(", corpus {}… over {} cases", &c[..16.min(c.len())], m["cases"])).unwrap_or_default();
o.push_str(&format!("member {:<18} {:<4} up to {} qubits, {} {}{corpus}\n", s(&m["key"]), s(&m["class"]), m["detail"]["max_qubits"], s(&m["implementation"]), s(&m["parity"]["tier"])));
o.push_str(&format!(" {}\n", s(&m["member"])));
}
for x in f["refused"].as_array().into_iter().flatten() {
o.push_str(&format!("not placed {} {}: {}\n", s(&x["backend"]), if x["key"].as_str().is_some_and(|k| !k.is_empty()) { s(&x["key"]) } else { s(&x["member"]) }, s(&x["why"])));
}
for x in f["unavailable"].as_array().into_iter().flatten() {
o.push_str(&format!("none found {}: {}\n", s(&x["backend"]), s(&x["why"])));
}
if f["backends"].as_array().is_none_or(Vec::is_empty) {
o.push_str("backends none in this build: the CPU alone (build with --features cuda, hip or opencl)\n");
}
o
}
fn die(msg: &str) -> ! {
eprintln!("wai-quantum: {msg}");
std::process::exit(1)
}
/// The program in `file`, or on standard input for `-` or none. A generator
/// (a program with integer `input`s) is written out at the `--input` values,
/// so every command reads one circuit of its family.
fn read_input(file: Option<&Path>) -> Result<String, String> {
at_inputs(read_program(file)?, &INPUTS.get().cloned().unwrap_or_default())
}
/// The format `--from` gives, for every program read.
static FROM: std::sync::OnceLock<Option<wai_quantum::engine::formats::Format>> = std::sync::OnceLock::new();
fn format_arg(s: &str) -> Result<wai_quantum::engine::formats::Format, String> {
wai_quantum::engine::formats::Format::parse(s).ok_or_else(|| format!("{s}: the formats are qasm, qir and jeff"))
}
/// The bytes of `file`, or standard input for `-` or none.
fn read_bytes(file: Option<&Path>) -> Result<Vec<u8>, String> {
match file {
Some(p) if p != Path::new("-") => std::fs::read(p).map_err(|e| format!("{}: {e}", p.display())),
_ => {
use std::io::Read as _;
let mut b = Vec::new();
std::io::stdin().read_to_end(&mut b).map_err(|e| format!("standard input: {e}"))?;
Ok(b)
}
}
}
/// The format a program is in: `--from`, else its file's extension (`.ll`
/// and `.qir` are QIR, `.jeff` is jeff), else OpenQASM.
fn format_of(file: Option<&Path>) -> wai_quantum::engine::formats::Format {
use wai_quantum::engine::formats::Format;
if let Some(Some(f)) = FROM.get() {
return *f;
}
file.and_then(|p| Format::from_extension(&p.to_string_lossy())).unwrap_or(Format::Qasm)
}
/// The program in `file` as OpenQASM: one in another format is read to it,
/// its notes on standard error.
fn read_program(file: Option<&Path>) -> Result<String, String> {
use wai_quantum::engine::formats::{self, Format};
let f = format_of(file);
if f == Format::Qasm {
return read_text(file);
}
let r = formats::read(f, &read_bytes(file)?).map_err(|d| d.to_string())?;
for n in &r.notes {
eprintln!("wai-quantum: {}: {n}", f.name());
}
Ok(r.qasm)
}
/// `text` as one circuit: a generator written out at `inputs`, any other
/// program as it is (and refused if it was given inputs it does not take).
fn at_inputs(text: String, inputs: &wai_quantum::qasm::Inputs) -> Result<String, String> {
if inputs.is_empty() && wai_quantum::qasm::inputs(&text).map(|v| v.is_empty()).unwrap_or(true) {
return Ok(text);
}
wai_quantum::qasm::instantiate(&text, inputs)
}
/// The text of `file`, or standard input for `-` or none.
fn read_text(file: Option<&Path>) -> Result<String, String> {
match file {
Some(p) if p != Path::new("-") => std::fs::read_to_string(p).map_err(|e| format!("{}: {e}", p.display())),
_ => {
use std::io::Read as _;
let mut s = String::new();
std::io::stdin().read_to_string(&mut s).map_err(|e| format!("standard input: {e}"))?;
Ok(s)
}
}
}
/// The job's program as one circuit, and its request: with `--history`, the
/// request carries the history's account of the version the file holds;
/// with `--intent`, what the program must do.
fn job_request(job: &Job) -> Result<Value, String> {
let text = read_program(job.file.as_deref())?;
let inputs = INPUTS.get().cloned().unwrap_or_default();
// A sweep sends the program with its inputs, which the sweep gives values.
if !job.sweep.is_empty() && !inputs.is_empty() {
return Err("give an input a fixed value as a sweep of one value (--sweep n=5), not with --input".into());
}
let program = if job.sweep.is_empty() { at_inputs(text.clone(), &inputs)? } else { text.clone() };
let mut req = request(job, program);
if !job.programs.is_empty() {
let mut all = vec![req["qasm"].take()];
for p in &job.programs {
all.push(json!(at_inputs(read_program(Some(p))?, &inputs)?));
}
req.as_object_mut().map(|o| o.remove("qasm"));
req["programs"] = Value::Array(all);
}
if !job.sweep.is_empty() {
req["sweep"] = Value::Object(job.sweep.iter().cloned().map(|(k, v)| (k, Value::Array(v))).collect());
}
if let Some(t) = &job.target {
req["target"] = targets::request_value(t)?;
}
if let Some(p) = &job.history {
req["history"] = history::attest(&history::load(p)?, &text, &inputs)?;
}
if !job.intent.is_empty() {
let mut all = Vec::new();
for f in &job.intent {
all.extend(intent_file(f)?);
}
req["intent"] = Value::Array(all);
}
Ok(req)
}
/// The intents a file gives, each reference read in as text and named by its
/// file: a program (`.qasm`, the same circuit), or JSON (a distribution of
/// the measured bits, an intent, a list of them, or a project's
/// `intent.json`, whose references are read beside it).
fn intent_file(path: &Path) -> Result<Vec<Value>, String> {
let text = read_text(Some(path))?;
let name = path.display().to_string();
if path.extension().is_some_and(|e| e == "qasm") {
return Ok(vec![json!({ "equivalent": at_inputs(text, &INPUTS.get().cloned().unwrap_or_default())?, "name": name })]);
}
let v: Value = serde_json::from_str(&text).map_err(|e| format!("{name}: an intent file is OpenQASM (.qasm) or JSON: {e}"))?;
let dir = path.parent().unwrap_or(Path::new("."));
let list = match &v {
Value::Array(a) => a.clone(),
Value::Object(o) if o.contains_key("intent") => o["intent"].as_array().cloned().ok_or_else(|| format!("{name}: \"intent\" is a list"))?,
Value::Object(o) if !o.is_empty() && o.keys().all(|k| !k.is_empty() && k.bytes().all(|b| b == b'0' || b == b'1')) => vec![json!({ "distribution": v })],
_ => vec![v.clone()],
};
list.into_iter()
.map(|mut i| {
for k in ["equivalent", "prepares"] {
if let Some(f) = i.get(k).and_then(Value::as_str).filter(|f| !f.contains(';')).map(str::to_string) {
let p = dir.join(&f);
i[k] = json!(at_inputs(read_text(Some(&p))?, &INPUTS.get().cloned().unwrap_or_default())?);
i["name"] = json!(f);
}
}
Ok(i)
})
.collect()
}
/// The job's request, with only the fields given, as the service receives it.
fn request(job: &Job, qasm: String) -> Value {
let mut r = json!({ "qasm": qasm });
if let Some(s) = job.shots {
r["shots"] = json!(s);
}
if let Some(s) = job.seed {
r["seed"] = json!(s);
}
if let Some(m) = &job.method {
r["method"] = json!(m);
}
if !job.observables.is_empty() {
r["observables"] = json!(job.observables);
}
if !job.amplitudes.is_empty() {
r["amplitudes"] = json!(job.amplitudes);
}
if !job.noise.is_empty() {
r["noise"] = Value::Object(job.noise.iter().map(|(k, v)| (k.clone(), json!(v))).collect());
}
if !job.options.is_empty() {
r["options"] = Value::Object(job.options.iter().cloned().collect());
}
if let Some(e) = job.compile {
r["compile"] = json!({ "epsilon": e });
}
if job.repair {
r["repair"] = match job.max_iterations {
Some(n) => json!({ "max_iterations": n }),
None => json!(true),
};
}
if job.verdict && job.intent.is_empty() {
r["verdict"] = json!(true);
}
if let Some(m) = &job.mitigate {
r["mitigate"] = if m.trim_start().starts_with('{') { serde_json::from_str(m).unwrap_or_else(|_| json!(m)) } else { json!(m) };
}
if let Some(p) = &job.pulse {
r["pulse"] = p.clone();
}
if !job.params.is_empty() {
r["params"] = Value::Object(job.params.iter().map(|(k, v)| (k.clone(), json!(v))).collect());
}
match &job.fabric[..] {
[] => {}
[one] => r["fabric"] = json!(one),
many => r["fabric"] = json!(many),
}
r
}
/// What a result says of its energy: the command line does not meter a run.
const UNMETERED: &str = "the command line does not meter a run's energy";
/// The job body `run` gives, as the service gives it: the request, the plan,
/// and the result sealed with `key` ([`job::run`]).
pub fn run_here(req: &Value, memory_gb: u64, key: &wai_quantum::ed25519_dalek::SigningKey) -> Result<Value, String> {
if catalog::is_set(req) {
return wai_quantum::engine::sets::run(req, memory_gb << 30, key, json!({ "unmetered": UNMETERED }));
}
job::run(req, memory_gb << 30, key, json!({ "unmetered": UNMETERED }))
}
/// What is new in a circuit as a reader takes it in.
pub(crate) fn novelty_text(v: &Value) -> String {
let mut out = String::new();
if let Some(n) = v["nearest"].as_object() {
out.push_str(&format!("nearest {} (distance {}, of {} compared)\n", n["name"].as_str().unwrap_or(""), n["distance"], v["compared"]));
}
let found = v["rediscovers"].as_array().cloned().unwrap_or_default();
if found.is_empty() {
out.push_str("rediscovers no known construction the engine checks\n");
}
for r in &found {
let what = if r["prepares"] == true { "the same state as" } else { "the same circuit as" };
let flag = if r["route"] == "a different route" { " ← a different route to a known result" } else { "" };
out.push_str(&format!("rediscovers {what} {} (by {}; distance {}){flag}\n", r["name"].as_str().unwrap_or(""), r["how"].as_str().unwrap_or(""), r["distance"]));
}
for s in v["unasked"].as_array().into_iter().flatten() {
out.push_str(&format!("unasked {}: {} against the reference's {}\n", s["measure"].as_str().unwrap_or(""), s["candidate"], s["reference"]));
}
out
}
/// The versions as a reader takes them in.
fn version_text(v: &Value) -> String {
let s = |p: &str| v.pointer(p).and_then(Value::as_str).unwrap_or("").to_string();
let mut out = format!("{} {}\nengine {} {}\n", s("/host/name"), s("/host/version"), s("/engine/name"), s("/engine/version"));
for (k, n) in v["schemas"].as_object().into_iter().flatten() {
out.push_str(&format!("schema {:<14}{}\n", k, n.as_str().unwrap_or("")));
}
let mcp: Vec<&str> = v.pointer("/protocols/mcp").and_then(Value::as_array).into_iter().flatten().filter_map(Value::as_str).collect();
out.push_str(&format!("mcp {}\n", mcp.join(", ")));
if let Some(p) = v.pointer("/protocols/provider").and_then(Value::as_str) {
out.push_str(&format!("providers {p}\n"));
}
out.push_str(&format!("policy {}\ndeprecations {}\n", s("/policy"), s("/deprecations")));
out
}
/// A critique as a reader takes it in.
fn print_critique(name: &str, c: &Value) {
let n = |k: &str| c[k].as_u64().unwrap_or(0);
println!("{name}: {} qubits, {} gates ({} one-qubit, {} two-qubit, {} three-qubit), depth {}, two-qubit depth {}", n("qubits"), n("gates"), n("one_qubit"), n("two_qubit"), n("three_qubit"), n("depth"), n("two_qubit_depth"));
let clifford = if c["clifford"] == true { "a Clifford circuit".to_string() } else { format!("{} gates off the Clifford angles", n("non_clifford")) };
println!(" {} T gates; {clifford}; {} qubits measured", n("t_count"), n("measured"));
let f = &c["fidelity_model"];
println!(" fidelity {:.4}, modelled: {}", f["estimate"].as_f64().unwrap_or(f64::NAN), f["note"].as_str().unwrap_or(""));
for s in c["smells"].as_array().into_iter().flatten() {
let fix = if s["fixable"] == true { " (improve fixes it)" } else { "" };
println!(" {}: {}{fix}", s["kind"].as_str().unwrap_or(""), s["message"].as_str().unwrap_or(""));
}
}
/// The classical routes as a reader takes them in.
fn print_shortcuts(v: &Value) {
println!("{}", v["summary"].as_str().unwrap_or(""));
println!();
println!("{:<15} {:>10} {:>9} {:>12} why", "route", "memory", "log2 ops", "at 10^10/s");
for r in v["routes"].as_array().into_iter().flatten() {
let name = r["route"].as_str().unwrap_or("");
let why = r["why"].as_str().unwrap_or("");
if r["applies"] != true || r.get("log2_work").is_none() {
let mark = if r["applies"] == true { "—" } else { "no" };
println!("{name:<15} {mark:>10} {:>9} {:>12} {why}", "", "");
continue;
}
let bytes = r["memory_bytes"].as_f64().unwrap_or(0.0);
let mem = if bytes < 1024.0 { format!("{bytes:.0} B") } else { format!("2^{:.1} B", bytes.log2()) };
let secs = r["seconds_at_reference"].as_f64().unwrap_or(f64::NAN);
let t = if secs < 1.0 { format!("{:.1e} s", secs) } else if secs < 3.15e7 { format!("{secs:.3e} s") } else { format!("{:.1e} y", secs / 3.15e7) };
println!("{name:<15} {mem:>10} {:>9.2} {t:>12} {why}", r["log2_work"].as_f64().unwrap_or(f64::NAN));
}
println!();
println!("{}", v["note"].as_str().unwrap_or(""));
}
/// A plan's or a result's `target` as a reader takes it in.
fn print_target(t: &Value) {
let p = &t["placement"];
let n = &t["noise"];
println!("target {} (calibration {}), hash {}", t["name"].as_str().unwrap_or(""), t["calibration"]["id"].as_str().unwrap_or("unnamed"), t["hash"].as_str().unwrap_or(""));
println!(
"placed on {} → {}, {} swaps, {} native gates in {}; expected success {:.4} (fewest swaps {:.4})",
p["layout"], p["final_layout"], p["swaps"], p["native_gates"], p["basis"].as_str().unwrap_or(""),
p["expected_success"].as_f64().unwrap_or(f64::NAN), p["fewest_swaps"]["expected_success"].as_f64().unwrap_or(f64::NAN)
);
println!("noise {} channels over {:.0} ns ({:.0} ns idle); {}", n["channels"], n["duration_ns"].as_f64().unwrap_or(0.0), n["idle_ns"].as_f64().unwrap_or(0.0), n["model"].as_str().unwrap_or(""));
for note in p["notes"].as_array().into_iter().flatten().chain(n["notes"].as_array().into_iter().flatten()) {
println!(" {}", note.as_str().unwrap_or(""));
}
}
/// A job body as a reader takes it in.
fn print_job(body: &Value, top: usize) {
let r = &body["result"];
let s = |v: &Value| v.as_str().map(str::to_string).unwrap_or_else(|| v.to_string());
if r["format"] == wai_quantum::engine::sets::FORMAT {
println!("set {} items, {} done, {} failed", r["count"], r["done"], r["failed"]);
for it in r["items"].as_array().into_iter().flatten() {
let point = it.get("point").map(|p| format!(" {p}")).unwrap_or_default();
println!("── item {}{point}", it["index"]);
match it["error"].as_str() {
Some(e) => println!("failed {e}"),
None => print_job(it, top),
}
}
let v = wai_quantum::engine::sets::verify(body);
println!("── the set");
println!("root {} (the Merkle root of every item's leaf)", s(&r["root"]));
if let Some(key) = r.pointer("/receipt/key").and_then(Value::as_str) {
println!("receipt {}, signed by {}", if v["valid"] == true { "verifies, every item sealed" } else { "DOES NOT VERIFY" }, job::did_of(key).unwrap_or_else(|| key.to_string()));
}
return;
}
println!("method {} — {}", s(&body["method"]), s(&body["why"]));
if let Some(acc) = r.get("accuracy") {
let mut extra: Vec<String> = acc.as_object().map(|o| o.iter().filter(|(k, _)| *k != "class" && *k != "note").map(|(k, v)| format!("{k} {}", s(v))).collect()).unwrap_or_default();
if let Some(n) = acc.get("note").and_then(Value::as_str) {
extra.push(n.to_string());
}
println!("accuracy {}{}", s(&acc["class"]), if extra.is_empty() { String::new() } else { format!(" ({})", extra.join("; ")) });
}
println!("circuit {} qubits, {} gates", r["qubits"], r["gates"]);
if let Some(f) = r.get("fabric") {
let s = |v: &Value| v.as_str().unwrap_or("").to_string();
match f.get("partitions").and_then(Value::as_array) {
Some(parts) => println!("fabric {} partitions on {}, {} exchanges, {}", parts.len(), parts.iter().map(|p| s(&p["key"])).collect::<Vec<_>>().join(", "), f["exchanges"], s(&f["parity"]["tier"])),
None => println!("fabric {} ({}), {}, certificate {}", s(&f["key"]), s(&f["implementation"]), s(&f["parity"]["tier"]), s(&f["certificate"])),
}
if f.get("auto").is_some() {
println!(" {}", s(&f["why"]));
}
}
if let Some(w) = r.get("warning").and_then(Value::as_str) {
println!("warning {w}");
}
if let Some(p) = r.get("global_phase").and_then(Value::as_str) {
println!("phase {p}");
}
if let Some(counts) = r.get("counts").and_then(Value::as_object) {
let total: u64 = counts.values().filter_map(Value::as_u64).sum::<u64>() + r["omitted_shots"].as_u64().unwrap_or(0);
println!("counts {total} shots, {} outcomes", r["distinct_outcomes"].as_u64().unwrap_or(counts.len() as u64));
let mut v: Vec<(&String, u64)> = counts.iter().map(|(k, c)| (k, c.as_u64().unwrap_or(0))).collect();
v.sort_by(|a, b| b.1.cmp(&a.1).then(a.0.cmp(b.0)));
for (k, c) in v.iter().take(top) {
println!(" {k} {c:>8} {:.4}", *c as f64 / total.max(1) as f64);
}
if v.len() > top {
println!(" … {} more (--top N)", v.len() - top);
}
}
// A bound that holds with a stated probability says so.
let held = |e: &Value| e.get("confidence").map(|c| format!(" (with probability {})", s(c))).unwrap_or_default();
for e in r.get("expectations").and_then(Value::as_array).into_iter().flatten() {
let bound = e.get("bound").map(|b| format!(" ± {}{}", s(b), held(e))).unwrap_or_default();
println!("⟨{}⟩ {}{bound}", s(&e["observable"]), s(&e["value"]));
}
for a in r.get("amplitudes").and_then(Value::as_array).into_iter().flatten() {
let (re, im) = (a["re"].as_f64().unwrap_or(f64::NAN), a["im"].as_f64().unwrap_or(f64::NAN));
let bound = a.get("bound").map(|b| format!(" ± {}{}", s(b), held(a))).unwrap_or_default();
println!("|{}⟩ {re:+.12} {}{:.12}i{bound}", s(&a["bits"]), if im < 0.0 { '-' } else { '+' }, im.abs());
}
if let Some(d) = r.get("dynamic") {
println!("dynamic {d}");
}
if let Some(m) = r.get("mitigation") {
println!("mitigation {}: {} circuits, {} shots", s(&m["method"]), m["cost"]["circuits"], m["cost"]["shots"]);
for o in m["observables"].as_array().into_iter().flatten() {
let f = |k: &str| o[k].as_f64().unwrap_or(f64::NAN);
println!(" ⟨{}⟩ raw {:+.5} ± {:.5} mitigated {:+.5} ± {:.5} (ideal {:+.5}, known in simulation)", s(&o["observable"]), f("raw"), f("raw_stderr"), f("mitigated"), f("stderr"), f("ideal"));
}
for d in m["distribution"].as_array().into_iter().flatten().take(top) {
println!(" {} raw {:.4} mitigated {:+.4} ± {:.4}", s(&d["bits"]), d["raw"].as_f64().unwrap_or(0.0), d["mitigated"].as_f64().unwrap_or(0.0), d["stderr"].as_f64().unwrap_or(0.0));
}
println!(" bias {}", s(&m["assumptions"]["bias"]));
}
if let Some(c) = r.get("compiled") {
println!("compiled {} at level {}: within {} of the program (budget {}), {} T gates; certificate {}", s(&c["basis"]), c["level"], s(&c["distance"]), s(&c["epsilon"]), c["output"]["t_count"], s(&c["certificate"]));
}
if let Some(t) = r.get("target") {
print_target(t);
if let Some(d) = r.pointer("/ideal/total_variation_distance").and_then(Value::as_f64) {
println!("from ideal total variation distance {d:.6}, Hellinger fidelity {:.6}", r["ideal"]["hellinger_fidelity"].as_f64().unwrap_or(f64::NAN));
}
}
if r.get("pulse").is_some_and(Value::is_object) {
print!("{}", pulse::report_run(r));
}
for x in r.pointer("/repaired/repairs").and_then(Value::as_array).into_iter().flatten() {
let lines: Vec<String> = x["edits"].as_array().into_iter().flatten().filter_map(|e| e["line"].as_u64()).map(|l| l.to_string()).collect();
println!("repaired {}: {} (line {})", x["code"].as_str().unwrap_or("—"), x["rule"].as_str().unwrap_or(""), lines.join(", "));
}
if let Some(v) = r.get("verdict") {
for (i, x) in v["intent"].as_array().into_iter().flatten().enumerate() {
let d = x["distance"].as_f64().map(|d| format!(", distance {d:.3e}")).unwrap_or_default();
println!("intent {:<4} {} by {} ({}{d}): {}", i + 1, x["outcome"].as_str().unwrap_or(""), x["how"].as_str().unwrap_or(""), x["strength"].as_str().unwrap_or(""), x["why"].as_str().unwrap_or(""));
}
println!("verdict {}: {}", v["level"].as_str().unwrap_or(""), v["statement"].as_str().unwrap_or(""));
}
if let Some(ms) = r.get("elapsed_ms") {
println!("time {ms} ms");
}
if let Some(key) = r.pointer("/receipt/key").and_then(Value::as_str) {
let ok = job::verify(body).map(|(ok, _)| ok).unwrap_or(false);
println!("receipt {}, signed by {}", if ok { "verifies" } else { "DOES NOT VERIFY" }, job::did_of(key).unwrap_or_else(|| key.to_string()));
}
}
fn load_key(k: &KeyArgs) -> keys::Key {
let key = keys::load(k.key.as_deref(), k.demo_key).unwrap_or_else(|e| die(&e));
keys::notes(&key);
key
}
/// A dry run as a person reads it.
#[cfg(feature = "service")]
fn print_dry_run(p: &Value) {
let s = |v: &Value| v.as_str().map(str::to_string).unwrap_or_else(|| v.to_string());
match p["accepted"].as_bool() {
Some(true) => println!("would run yes, now"),
_ => println!("would run no: {}", s(&p["refusal"]["message"])),
}
if let Some(m) = p["method"].as_str() {
println!("method {m} — {}", s(&p["why"]));
println!("memory {:.1} MiB at most", p["memory_bytes"].as_f64().unwrap_or(0.0) / (1u64 << 20) as f64);
match p["time"]["seconds"].as_f64() {
Some(t) => println!("time about {t:.3} s ({})", s(&p["time"]["basis"])),
None => println!("time not estimated: {} (at most {} s)", s(&p["time"]["why"]), p["time"]["at_most"]),
}
match p["energy"]["joules_micro_estimate"].as_u64() {
Some(u) => println!("energy about {:.3} J, an estimate ({})", u as f64 / 1e6, s(&p["energy"]["basis"])),
None => println!("energy not estimated: {}", s(&p["energy"]["unestimated"])),
}
}
for it in p["items"].as_array().into_iter().flatten() {
let point = it.get("point").map(|x| format!(" {x}")).unwrap_or_default();
println!("item {:<6} {}{point}, about {} s", it["index"], s(&it["method"]), it["time"]["seconds"]);
}
let b = &p["bounds"];
println!("bounds {} MiB a job, {} s a job, {} at once, {} submissions in {} s", b["job_memory_bytes"].as_u64().unwrap_or(0) >> 20, b["job_seconds"], b["active_jobs_per_client"], b["submissions_per_window"], b["window_seconds"]);
println!("you {} active, {} submissions left in this window", p["client"]["active_jobs"], p["client"]["submissions_left"]);
}
/// `convert`: the program in `file` written as `to`, to `output` or standard
/// output, its notes and the check on standard error (or all of it as JSON).
fn convert(file: Option<&Path>, to: wai_quantum::engine::formats::Format, profile: Option<&str>, output: Option<&Path>, as_json: bool, no_check: bool) {
use std::io::{IsTerminal, Write as _};
use wai_quantum::engine::formats::{self, Format, Profile};
let bytes = read_bytes(file).unwrap_or_else(|e| die(&e));
let from = match FROM.get() {
Some(Some(f)) => *f,
_ => file.and_then(|p| Format::from_extension(&p.to_string_lossy())).unwrap_or_else(|| Format::sniff(&bytes)),
};
let profile = profile.map(|p| Profile::parse(p).unwrap_or_else(|| die(&format!("{p}: the QIR profiles are base and adaptive"))));
let c = formats::convert(from, to, &bytes, profile, !no_check).unwrap_or_else(|d| die(&d.to_string()));
let verdict = c.check["verdict"]["verdict"].as_str().unwrap_or("");
if as_json {
let mut v = json!({
"ok": true, "format": formats::FORMAT, "from": from.name(), "to": to.name(), "qasm": c.qasm,
"notes": c.notes.iter().map(|d| d.to_json()).collect::<Vec<_>>(), "read": c.read, "written": c.written, "check": c.check,
});
if to.is_binary() {
v["program_base64"] = json!(formats::encode_base64(&c.output));
} else {
v["program"] = json!(String::from_utf8_lossy(&c.output));
}
if let Some(o) = output {
std::fs::write(o, &c.output).unwrap_or_else(|e| die(&format!("{}: {e}", o.display())));
}
println!("{v}");
} else {
for n in &c.notes {
eprintln!("note: {n}");
}
if !c.check.is_null() {
eprintln!("check: {} ({}): {}", verdict, c.check["verdict"]["how"].as_str().unwrap_or(""), c.check["what"].as_str().unwrap_or(""));
}
match output {
Some(o) => std::fs::write(o, &c.output).unwrap_or_else(|e| die(&format!("{}: {e}", o.display()))),
None if to.is_binary() && std::io::stdout().is_terminal() => die("jeff is binary: give -o FILE, or --json for its base64"),
None => {
let mut out = std::io::stdout();
out.write_all(&c.output).and_then(|_| out.flush()).unwrap_or_else(|e| die(&format!("standard output: {e}")));
}
}
}
if verdict == "different" {
std::process::exit(2);
}
}
fn main() {
// The help a parse prints is in the person's language, so the language is read first.
let args: Vec<String> = std::env::args().collect();
let loc = locale::resolve(locale::arg_lang(&args).as_deref());
locale::set(loc);
let cli = Cli::from_arg_matches(&locale::localize(Cli::command(), loc).get_matches_from(&args)).unwrap_or_else(|e| e.exit());
let _ = INPUTS.set(cli.inputs.iter().cloned().collect());
let _ = FROM.set(cli.from);
match cli.command {
#[cfg(feature = "providers")]
Command::Run { job, out, key, provider } if provider.provider.is_some() => providers::run(&job, &out, &key, &provider),
Command::Run { job, out, key, .. } => {
let req = job_request(&job).unwrap_or_else(|e| die(&e));
let k = load_key(&key);
let body = match run_here(&req, job.memory_gb, &k.key) {
Ok(b) => b,
// A job that asked for a verdict is refused with one.
Err(e) if wai_quantum::engine::verdict::asked(&req) || job.repair => {
let v = wai_quantum::engine::verdict::of_refusal(&req, &e);
if out.json {
println!("{}", json!({ "error": e, "verdict": v }));
} else {
eprintln!("verdict {}: {}", v["level"].as_str().unwrap_or(""), v["statement"].as_str().unwrap_or(""));
}
die(&e)
}
Err(e) => die(&e),
};
if out.json {
println!("{body}");
} else {
print_job(&body, out.top);
}
// With an intent: exit 0 it holds, 1 it fails, 2 it is unchecked;
// for a set, every item's intent.
if !job.intent.is_empty() {
let holds: Vec<Option<bool>> = match body.pointer("/result/items").and_then(Value::as_array) {
Some(items) => items.iter().map(|i| i.pointer("/result/verdict/holds").and_then(Value::as_bool)).collect(),
None => vec![body.pointer("/result/verdict/holds").and_then(Value::as_bool)],
};
std::process::exit(if holds.contains(&Some(false)) {
1
} else if holds.iter().all(|h| *h == Some(true)) {
0
} else {
2
});
}
}
Command::Plan { job, json } if !job.programs.is_empty() || !job.sweep.is_empty() => {
let req = job_request(&job).unwrap_or_else(|e| die(&e));
let items = wai_quantum::engine::sets::expand(&req).unwrap_or_else(|e| die(&e));
let plans = wai_quantum::engine::sets::plan(&items, job.memory_gb << 30).unwrap_or_else(|e| die(&e));
if json {
println!("{}", json!({ "set": serde_json::to_value(wai_quantum::engine::sets::summary(&plans)).unwrap_or(Value::Null), "items": items.iter().zip(&plans).map(|(i, p)| json!({ "point": i.point, "plan": p })).collect::<Vec<_>>() }));
} else {
let sum = wai_quantum::engine::sets::summary(&plans);
println!("set {} items: {}", plans.len(), sum.why);
println!("memory {:.1} MiB at most (the largest item)", sum.memory_bytes as f64 / (1u64 << 20) as f64);
for (k, (i, p)) in items.iter().zip(&plans).enumerate() {
let point = i.point.as_ref().map(|p| format!(" {p}")).unwrap_or_default();
println!("item {k:<6} {}{point} — {}", p.method, p.why);
}
}
}
Command::Plan { job, json } => {
let req = job_request(&job).unwrap_or_else(|e| die(&e));
let p = catalog::plan(&req, job.memory_gb << 30).unwrap_or_else(|e| die(&e));
if json {
println!("{}", serde_json::to_value(&p).unwrap_or(Value::Null));
} else {
println!("method {}", p.method);
println!("why {}", p.why);
println!("qubits {}", p.qubits);
println!("memory {:.1} MiB at most", p.memory_bytes as f64 / (1u64 << 20) as f64);
println!("work {}", p.work);
match catalog::nominal_rate(&p.method) {
Some(rate) => println!("time about {:.3} s at the planner's nominal rate (an estimate, not measured here)", p.work as f64 / rate),
None => println!("time not estimated: {}'s work does not predict its time", p.method),
}
if let Some(t) = &p.target {
print_target(t);
}
}
}
Command::Instance { file } => {
print!("{}", read_input(file.as_deref()).unwrap_or_else(|e| die(&e)));
}
Command::Convert { file, to, profile, output, json, no_check } => convert(file.as_deref(), to, profile.as_deref(), output.as_deref(), json, no_check),
Command::Lint { file, json } => {
let name = file.as_deref().filter(|p| *p != Path::new("-")).map(|p| p.display().to_string()).unwrap_or_else(|| "<stdin>".into());
let src = read_input(file.as_deref()).unwrap_or_else(|e| die(&e));
let v = job::lint(&src);
if json {
println!("{v}");
} else if v["ok"] == true {
let dynamic = v["dynamic_steps"].as_u64().map(|s| format!(", dynamic: {s} steps")).unwrap_or_default();
println!("{name}: ok — {} qubits, {} gates, {} bits measured{dynamic}", v["qubits"], v["gates"], v["measured"]);
} else {
println!("{name}:{}", v["error"].as_str().unwrap_or("").trim_start_matches("line "));
}
if v["ok"] != true {
std::process::exit(1);
}
}
Command::Critique { file, json } => {
let name = file.as_deref().filter(|p| *p != Path::new("-")).map(|p| p.display().to_string()).unwrap_or_else(|| "<stdin>".into());
let src = read_input(file.as_deref()).unwrap_or_else(|e| die(&e));
let c = critique::critique(&src);
if json {
println!("{c}");
} else if c["ok"] == true {
print_critique(&name, &c);
} else {
die(c["error"].as_str().unwrap_or("the program does not read"));
}
if c["ok"] != true {
std::process::exit(1);
}
}
Command::Equivalent { a, b, json } => {
let (x, y) = (read_input(Some(&a)).unwrap_or_else(|e| die(&e)), read_input(Some(&b)).unwrap_or_else(|e| die(&e)));
let v = equiv::equivalent(&x, &y);
if json {
println!("{v}");
} else {
println!("{}: {} ({})", v["verdict"].as_str().unwrap_or(""), v["why"].as_str().unwrap_or(""), v["how"].as_str().unwrap_or(""));
}
std::process::exit(match v["verdict"].as_str() {
Some("equivalent") => 0,
Some("different") => 1,
_ => 2,
});
}
Command::Improve { file, json } => {
let src = read_input(file.as_deref()).unwrap_or_else(|e| die(&e));
let v = critique::variants(&src);
if v["ok"] != true {
die(v["error"].as_str().unwrap_or("the program does not read"));
}
if json {
println!("{v}");
return;
}
match v["candidates"].as_array().and_then(|c| c.first()) {
Some(c) => {
for ch in c["changes"].as_array().into_iter().flatten() {
eprintln!("fixed: {}", ch.as_str().unwrap_or(""));
}
let (b, m) = (&v["base"], &c["measures"]);
eprintln!("gates {} → {}, two-qubit {} → {}, depth {} → {}", b["gates"], m["gates"], b["two_qubit"], m["two_qubit"], b["depth"], m["depth"]);
eprintln!("checked: {}", c["equivalence"]["why"].as_str().unwrap_or(""));
print!("{}", c["qasm"].as_str().unwrap_or(""));
}
None => {
eprintln!("nothing to improve that the engine can check: no gates cancel or merge; the program is unchanged");
print!("{src}");
}
}
}
Command::Shortcuts { file, thorough, json } => {
let src = read_input(file.as_deref()).unwrap_or_else(|e| die(&e));
let v = shortcuts::shortcuts(&src, thorough);
if v["ok"] != true {
die(v["error"].as_str().unwrap_or("the program does not read"));
}
if json {
println!("{v}");
} else {
print_shortcuts(&v);
}
}
Command::Draw { file, width, svg, latex, describe } => {
let src = read_input(file.as_deref()).unwrap_or_else(|e| die(&e));
let p = wai_quantum::qasm::parse(&src).unwrap_or_else(|e| die(&e));
if svg {
print!("{}", wai_quantum::engine::svg::circuit::<&str>(&p, &[]));
} else if latex {
print!("{}", wai_quantum::engine::latex::circuit(&p));
} else if describe {
println!("{}", wai_quantum::engine::describe::describe(&p));
} else {
print!("{}", draw::draw(&p, width));
}
}
Command::Debug(a) => debug::main(&a).unwrap_or_else(|e| die(&e)),
Command::Dap => std::process::exit(dap::serve()),
#[cfg(feature = "service")]
Command::Submit { job, out, service, dry_run, detach, api_key } => {
let req = job_request(&job).unwrap_or_else(|e| die(&e));
let c = service::Client::new(&service, api_key);
if dry_run {
let plan = c.dry_run(&req).unwrap_or_else(|e| die(&e));
if out.json {
println!("{plan}");
} else {
print_dry_run(&plan);
}
if plan["accepted"] != true {
std::process::exit(1);
}
return;
}
let admitted = c.submit(&req).unwrap_or_else(|e| die(&e));
let id = admitted["id"].as_str().unwrap_or_else(|| die("the service gave no job id")).to_string();
if detach {
println!("{admitted}");
return;
}
let body = c.follow(&id, |s| eprintln!("job {id}: {s}")).unwrap_or_else(|e| die(&e));
if body["status"] != "done" {
die(&format!("the job {}: {}", body["status"].as_str().unwrap_or("failed"), body["error"].as_str().unwrap_or("")));
}
if out.json {
println!("{body}");
} else {
print_job(&body, out.top);
}
}
#[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) {
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 c["valid"] != true {
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()));
}
if let Some(c) = v["request"].get("history") {
let hands: Vec<String> = c["gates"].as_object().into_iter().flatten().map(|(h, n)| format!("{n} {}", h.replace('_', " "))).collect();
println!("history {}: {} of {} gates", c["version"].as_str().unwrap_or("?"), hands.join(", "), c["of"]["gates"]);
}
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));
println!("account {}", if r["ok"] == true { "the history's own, recomputed from it".to_string() } else { format!("NOT the history's: {}", r["problems"]) });
ok &= r["ok"] == true;
}
if !ok {
std::process::exit(1);
}
} else {
lab::verify_receipt(&src);
}
}
Command::Key { key } => {
let k = load_key(&key);
println!("{}", wai_quantum::quantum_receipt::did_key(&k.key.verifying_key()));
match &k.path {
Some(p) => eprintln!("key file {}", p.display()),
None => eprintln!("the published demo key"),
}
}
Command::Version { json } => {
let v = mcp::versions();
if json {
println!("{v}");
} else {
print!("{}", version_text(&v));
}
}
Command::Methods { json, memory_gb } => {
let m = catalog::describe(memory_gb << 30);
if json {
println!("{m}");
} else {
for x in m.as_array().into_iter().flatten() {
let reach = if x.get("available") == Some(&Value::Bool(false)) { "not in this build".to_string() } else { format!("up to {} qubits", x["max_qubits"]) };
println!("{:<20} {reach:<20} {}", x["method"].as_str().unwrap_or(""), x["accuracy"].as_str().unwrap_or(""));
println!("{:<20} {}", "", x["circuits"].as_str().unwrap_or(""));
}
}
}
Command::Fabric { json, memory_gb } => {
let f = wai_quantum::engine::fabric::list(memory_gb << 30);
if json {
println!("{f}");
} else {
print!("{}", fabric_text(&f));
}
}
Command::Components { json } => {
let c = wai_quantum::engine::registry::Registry::builtins().components();
if json {
println!("{c}");
} else {
print!("{}", components_text(&c));
}
}
Command::Diagnostics { json } => {
if json {
println!("{}", job::diagnostic_codes());
} else {
for c in wai_quantum::qasm::diag::codes() {
println!("{} {:<9} {}", c.code, c.outcome.as_str(), c.meaning);
}
for c in wai_quantum::engine::refusal::codes() {
println!("{} {:<9} {}", c.code, c.outcome.as_str(), c.meaning);
}
}
}
Command::Mcp { cmd: Some(cmd), .. } => guide::mcp(&cmd).unwrap_or_else(|e| die(&e)),
Command::Mcp { cmd: None, memory_gb, key } => {
let k = load_key(&key);
mcp::serve(memory_gb, &k.key);
}
Command::New(a) => print!("{}", project::new(&a).unwrap_or_else(|e| die(&e))),
Command::Test(t) => std::process::exit(project::main(&t)),
Command::SetupAgent(a) => std::process::exit(guide::setup_agent(&a).unwrap_or_else(|e| die(&e))),
Command::Docs(d) => guide::docs(&d).unwrap_or_else(|e| die(&e)),
Command::Learn(a) => std::process::exit(learn::main(a).unwrap_or_else(|e| die(&e))),
Command::Fmt { files, check, write } => std::process::exit(fmt::main(&files, check, write)),
Command::Lsp { memory_gb, key, stdio: _ } => {
let k = load_key(&key);
std::process::exit(lsp::serve(memory_gb, k.key));
}
Command::Compile(a) => compile::main(a),
Command::Estimate(a) => estimate::main(a),
c @ (Command::Gradient(_) | Command::Optimize(_) | Command::Vqe(_) | Command::Qaoa(_) | Command::Build(_)) => variational::main(&c),
Command::Repair(a) => repair::main(a),
Command::Eval { cmd } => eval::main(&cmd).unwrap_or_else(|e| die(&e)),
Command::Discover { cmd } => discover::main(cmd),
Command::History { cmd } => history::main(cmd),
Command::Locks { cmd } => locks::main(cmd),
Command::Taste { cmd } => taste::main(cmd),
Command::Rewrite { cmd } => rewrite::main(cmd),
Command::Converge { cmd } => converge::main(cmd),
Command::Study { cmd } => study::main(cmd),
Command::Novelty { file, against, reference, weigh, json } => {
let inputs = INPUTS.get().cloned().unwrap_or_default();
let text = read_text(file.as_deref()).unwrap_or_else(|e| die(&e));
let others: Vec<(String, String)> = against.iter().map(|p| (p.display().to_string(), read_text(Some(p)).unwrap_or_else(|e| die(&e)))).collect();
let reference = reference.map(|p| read_text(Some(&p)).unwrap_or_else(|e| die(&e)));
let v = wai_quantum::engine::novelty::assess(&text, &inputs, &others, reference.as_deref(), &weigh);
if v["ok"] != true {
die(v["error"].as_str().unwrap_or("the program does not read"));
}
if json {
println!("{v}");
} else {
print!("{}", novelty_text(&v));
}
}
Command::Qec { cmd } => qec::main(cmd),
Command::Pulse { cmd } => pulse::main(cmd),
Command::Lab { args } => lab::main(args),
Command::Targets { cmd } => targets::main(cmd),
#[cfg(feature = "providers")]
Command::Providers { cmd } => providers::main(cmd),
Command::Completions { shell } => {
clap_complete::generate(shell, &mut Cli::command(), "wai-quantum", &mut std::io::stdout());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_generator_is_read_at_its_inputs_and_a_plain_program_as_it_is() {
let ghz = "OPENQASM 3.0;\ninput uint n;\nqubit[n] q;\nh q[0];\nfor uint i in [0:n-2] { cx q[i], q[i + 1]; }\n";
let at = |text: &str, pairs: &[(&str, i64)]| at_inputs(text.to_string(), &pairs.iter().map(|(k, v)| (k.to_string(), *v)).collect());
let three = at(ghz, &[("n", 3)]).unwrap();
assert_eq!(three, "OPENQASM 3.0;\nqubit[3] q;\nh q[0];\ncx q[0], q[1];\ncx q[1], q[2];\n");
// The instance runs as the service would run it: twelve qubits, one
// GHZ state, every shot all zeros or all ones.
let (mut j, _) = job("");
j.shots = Some(200);
let key = wai_quantum::ed25519_dalek::SigningKey::from_bytes(&[3; 32]);
let body = run_here(&request(&j, at(ghz, &[("n", 12)]).unwrap()), 1, &key).unwrap();
let counts = body["result"]["counts"].as_object().unwrap();
assert!(counts.keys().all(|k| k == &"0".repeat(12) || k == &"1".repeat(12)), "{counts:?}");
let plain = "OPENQASM 3.0;\nqubit q;\nh q;\n";
assert_eq!(at(plain, &[]).unwrap(), plain);
assert!(at(ghz, &[]).unwrap_err().contains("give n a value"));
assert!(at(plain, &[("n", 2)]).unwrap_err().contains("no `input n`"));
}
/// A set's request is the program and the others in order, the options
/// shared; a sweep's carries the program as written, inputs and all.
#[test]
fn a_set_and_a_sweep_are_requested_as_the_service_reads_them() {
let dir = std::env::temp_dir().join(format!("wai-q-cli-set-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let (a, b) = (dir.join("a.qasm"), dir.join("b.qasm"));
std::fs::write(&a, "OPENQASM 3.0; qubit q; h q;").unwrap();
std::fs::write(&b, "OPENQASM 3.0;\ninput float theta;\nqubit q;\nrx(theta) q;\n").unwrap();
let (mut j, _) = job("");
j.file = Some(a.clone());
j.shots = Some(8);
j.programs = vec![a.clone()];
let r = job_request(&j).unwrap();
assert_eq!(r, json!({ "programs": ["OPENQASM 3.0; qubit q; h q;", "OPENQASM 3.0; qubit q; h q;"], "shots": 8 }));
let body = run_here(&r, 1, &wai_quantum::ed25519_dalek::SigningKey::from_bytes(&[2; 32])).unwrap();
assert_eq!(wai_quantum::engine::sets::verify(&body)["valid"], true);
let (mut j, _) = job("");
j.file = Some(b.clone());
j.observables = vec!["Z0".into()];
j.sweep = vec![("theta".into(), vec![json!(0.0), json!(1.0)])];
let r = job_request(&j).unwrap();
assert_eq!(r["sweep"], json!({ "theta": [0.0, 1.0] }));
assert!(r["qasm"].as_str().unwrap().contains("input float theta;"));
let body = run_here(&r, 1, &wai_quantum::ed25519_dalek::SigningKey::from_bytes(&[2; 32])).unwrap();
assert!((body["result"]["items"][1]["result"]["expectations"][0]["value"].as_f64().unwrap() - 1f64.cos()).abs() < 1e-12);
let _ = std::fs::remove_dir_all(&dir);
}
fn job(qasm: &str) -> (Job, String) {
(
Job { file: None, shots: None, seed: None, method: None, observables: vec![], amplitudes: vec![], noise: vec![], options: vec![], memory_gb: 1, history: None, compile: None, target: None, intent: vec![], verdict: false, repair: false, max_iterations: None, programs: vec![], sweep: vec![], mitigate: None, pulse: None, params: vec![], fabric: vec![] },
qasm.to_string(),
)
}
/// `--intent` takes a reference program, a distribution, an intent, a
/// list, or a project's intent.json, each reference read beside it.
#[test]
fn an_intent_file_is_a_reference_a_distribution_or_intents() {
let d = std::env::temp_dir().join(format!("wai-quantum-intent-{}", std::process::id()));
std::fs::create_dir_all(&d).unwrap();
let bell = "OPENQASM 3.0;\nqubit[2] q;\nbit[2] c;\nh q[0];\ncx q[0], q[1];\nc = measure q;\n";
std::fs::write(d.join("ref.qasm"), bell).unwrap();
std::fs::write(d.join("dist.json"), r#"{"00": 0.5, "11": 0.5}"#).unwrap();
std::fs::write(d.join("list.json"), r#"[{"prepares": "ref.qasm", "note": "the pair"}, {"distribution": {"00": 0.5, "11": 0.5}, "tolerance": 1e-6}]"#).unwrap();
std::fs::write(d.join("intent.json"), r#"{"program": "x.qasm", "intent": [{"equivalent": "ref.qasm"}]}"#).unwrap();
let r = intent_file(&d.join("ref.qasm")).unwrap();
assert_eq!(r[0]["equivalent"], bell);
assert_eq!(intent_file(&d.join("dist.json")).unwrap()[0]["distribution"]["11"], 0.5);
let l = intent_file(&d.join("list.json")).unwrap();
assert_eq!((l[0]["prepares"].as_str(), l[0]["name"].as_str(), l.len()), (Some(bell), Some("ref.qasm"), 2));
assert_eq!(intent_file(&d.join("intent.json")).unwrap()[0]["equivalent"], bell);
// Run with all of them: the verdict, sealed.
let (mut j, q) = job(bell);
j.shots = Some(20);
let mut req = request(&j, q);
req["intent"] = Value::Array(["ref.qasm", "dist.json", "list.json"].iter().flat_map(|f| intent_file(&d.join(f)).unwrap()).collect());
let key = wai_quantum::ed25519_dalek::SigningKey::from_bytes(&[3; 32]);
let body = run_here(&req, 1, &key).unwrap();
assert_eq!((body["result"]["verdict"]["level"].as_str(), body["result"]["verdict"]["intent"].as_array().map(Vec::len)), (Some("proof"), Some(4)), "{body}");
assert!(job::verify(&body).unwrap().0);
assert!(intent_file(&d.join("missing.json")).is_err());
let _ = std::fs::remove_dir_all(&d);
}
#[test]
fn a_run_here_is_the_services_job_and_its_receipt_verifies() {
let (mut j, q) = job("OPENQASM 3.0; qubit[2] q; h q[0]; cx q[0], q[1];");
j.shots = Some(1000);
j.observables = vec!["Z0 Z1".into()];
let req = request(&j, q);
// Only what was given is in the request.
assert_eq!(req.as_object().unwrap().keys().collect::<Vec<_>>(), ["qasm", "shots", "observables"]);
let key = wai_quantum::ed25519_dalek::SigningKey::from_bytes(&[3; 32]);
let body = run_here(&req, 1, &key).unwrap();
assert_eq!(body["method"], "stabilizer");
assert_eq!(body["result"]["expectations"][0]["value"], 1.0);
let counts = body["result"]["counts"].as_object().unwrap();
assert!(counts.keys().all(|k| k == "00" || k == "11"), "{counts:?}");
assert!(job::verify(&body).unwrap().0);
// The result changed, or the request, and the receipt no longer holds.
let mut forged = body.clone();
forged["result"]["expectations"][0]["value"] = json!(0.5);
assert!(!job::verify(&forged).unwrap().0);
let mut forged = body.clone();
forged["request"]["shots"] = json!(999);
assert!(!job::verify(&forged).unwrap().0);
// A JSON writer's round trip leaves it verifying.
let again: Value = serde_json::from_str(&body.to_string()).unwrap();
assert!(job::verify(&again).unwrap().0);
}
#[test]
fn noise_and_options_are_read_and_a_misspelt_channel_is_refused() {
assert_eq!(noise_arg("depolarizing_1q=0.01").unwrap(), ("depolarizing_1q".into(), 0.01));
assert!(noise_arg("depolarizing=0.01").unwrap_err().contains("no such channel"));
assert!(noise_arg("readout").is_err());
assert_eq!(option_arg("max_bond=128").unwrap().1, json!(128));
assert_eq!(option_arg("x=true").unwrap().1, json!(true));
assert_eq!(option_arg("kind=fast").unwrap().1, json!("fast"));
}
#[cfg(feature = "service")]
#[test]
fn the_service_s_commands_parse_as_documented() {
// A set, a dry run and a detached submission, and the task commands.
match Cli::try_parse_from(["wai-quantum", "submit", "a.qasm", "--program", "b.qasm", "--program", "c.qasm", "--shots", "10", "--dry-run"]).unwrap().command {
Command::Submit { job, dry_run, detach, .. } => assert!(job.programs.len() == 2 && dry_run && !detach),
_ => panic!("not submit"),
}
assert!(Cli::try_parse_from(["wai-quantum", "submit", "p.qasm", "--dry-run", "--detach"]).is_err());
for t in [vec!["task", "target"], vec!["task", "acquire", "--lease", "l.json"], vec!["task", "start", "job.json"], vec!["task", "wait", "abc"], vec!["task", "result", "abc", "--service", "http://127.0.0.1:1"], vec!["task", "release"]] {
let argv: Vec<&str> = std::iter::once("wai-quantum").chain(t.iter().copied()).collect();
assert!(matches!(Cli::try_parse_from(&argv).unwrap().command, Command::Task { .. }), "{t:?}");
}
}
#[test]
fn the_command_line_parses_as_documented() {
Cli::command().debug_assert();
let c = Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--shots", "10", "-o", "Z0", "-o", "X1 X2", "--noise", "readout=0.02", "--option", "max_bond=8", "--json"]).unwrap();
match c.command {
Command::Run { job, out, .. } => {
assert_eq!(job.shots, Some(10));
assert_eq!(job.observables, ["Z0", "X1 X2"]);
assert_eq!(job.noise, [("readout".to_string(), 0.02)]);
assert!(out.json);
}
_ => panic!("not run"),
}
let c = Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--intent", "ref.qasm", "--intent", "dist.json", "--repair", "--max-iterations", "8"]).unwrap();
match c.command {
Command::Run { job, .. } => assert!(job.intent.len() == 2 && job.repair && job.max_iterations == Some(8)),
_ => panic!("not run"),
}
assert!(Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--max-iterations", "8"]).is_err(), "a bound needs --repair");
// Parameters on a job, and the variational and algorithm commands.
match Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--param", "theta=0.3", "--param", "phi=-1e-3"]).unwrap().command {
Command::Run { job, .. } => {
assert_eq!(job.params, [("theta".to_string(), 0.3), ("phi".to_string(), -1e-3)]);
assert_eq!(request(&job, "x".into())["params"], json!({ "theta": 0.3, "phi": -1e-3 }));
}
_ => panic!("not run"),
}
assert!(Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--param", "theta"]).is_err());
assert!(matches!(Cli::try_parse_from(["wai-quantum", "gradient", "p.qasm", "--param", "t=1", "--term", "Z0", "--gradient", "parameter_shift"]).unwrap().command, Command::Gradient(_)));
assert!(matches!(Cli::try_parse_from(["wai-quantum", "optimize", "p.qasm", "--param", "t=1", "--term", "Z0", "--optimizer", "spsa", "--steps", "5", "--seed", "2"]).unwrap().command, Command::Optimize(_)));
assert!(matches!(Cli::try_parse_from(["wai-quantum", "vqe", "--term", "Z0", "--layers", "2"]).unwrap().command, Command::Vqe(_)));
assert!(matches!(Cli::try_parse_from(["wai-quantum", "qaoa", "--graph", "g.json", "--layers", "2"]).unwrap().command, Command::Qaoa(_)));
assert!(matches!(Cli::try_parse_from(["wai-quantum", "build", "qsvt", "--phases", "0.1,-0.2", "--x", "-0.4"]).unwrap().command, Command::Build(_)));
assert!(matches!(Cli::try_parse_from(["wai-quantum", "repair", "p.qasm", "--size", "q=3", "--max-iterations", "4", "--json"]).unwrap().command, Command::Repair(_)));
// An unknown flag is an error, not ignored.
assert!(Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--shot", "10"]).is_err());
assert!(matches!(Cli::try_parse_from(["wai-quantum", "equivalent", "a.qasm", "b.qasm", "--json"]).unwrap().command, Command::Equivalent { json: true, .. }));
assert!(matches!(Cli::try_parse_from(["wai-quantum", "improve", "-"]).unwrap().command, Command::Improve { json: false, .. }));
assert!(matches!(Cli::try_parse_from(["wai-quantum", "shortcuts", "p.qasm", "--thorough"]).unwrap().command, Command::Shortcuts { thorough: true, json: false, .. }));
assert!(matches!(Cli::try_parse_from(["wai-quantum", "fmt", "a.qasm", "b.qasm", "--check"]).unwrap().command, Command::Fmt { check: true, write: false, .. }));
assert!(Cli::try_parse_from(["wai-quantum", "fmt", "a.qasm", "--check", "--write"]).is_err());
assert!(matches!(Cli::try_parse_from(["wai-quantum", "lsp", "--stdio", "--demo-key"]).unwrap().command, Command::Lsp { stdio: true, .. }));
assert!(matches!(Cli::try_parse_from(["wai-quantum", "draw", "p.qasm", "--svg"]).unwrap().command, Command::Draw { svg: true, latex: false, .. }));
assert!(Cli::try_parse_from(["wai-quantum", "draw", "p.qasm", "--svg", "--latex"]).is_err());
match Cli::try_parse_from(["wai-quantum", "debug", "p.qasm", "--line", "9", "--outcomes", "0,1", "--qubits", "0,2", "--noise", "readout=0.01"]).unwrap().command {
Command::Debug(a) => assert_eq!((a.line, a.outcomes.as_deref(), a.qubits, a.noise.len()), (Some(9), Some("0,1"), vec![0, 2], 1)),
_ => panic!("not debug"),
}
assert!(Cli::try_parse_from(["wai-quantum", "debug", "p.qasm", "--branches", "--to", "3"]).is_err());
assert!(matches!(Cli::try_parse_from(["wai-quantum", "dap"]).unwrap().command, Command::Dap));
match Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--target", "synthetic-line-5"]).unwrap().command {
Command::Run { job, .. } => assert_eq!(job.target.as_deref(), Some("synthetic-line-5")),
_ => panic!("not run"),
}
assert!(Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--target", "x", "--noise", "readout=0.1"]).is_err(), "a target's noise is its own");
// A set and a sweep (the service's submit and task commands are in their own test).
match Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--sweep", "theta=0.1,0.25,-1", "--sweep", "n=2,3"]).unwrap().command {
Command::Run { job, .. } => assert_eq!(job.sweep, vec![("theta".to_string(), vec![json!(0.1), json!(0.25), json!(-1)]), ("n".to_string(), vec![json!(2), json!(3)])]),
_ => panic!("not run"),
}
assert!(Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--sweep", "theta=x"]).is_err());
assert!(Cli::try_parse_from(["wai-quantum", "run", "p.qasm", "--sweep", "n=2", "--program", "b.qasm"]).is_err(), "a set or a sweep, not both");
assert!(matches!(Cli::try_parse_from(["wai-quantum", "targets", "show", "synthetic-line-5", "--view"]).unwrap().command, Command::Targets { .. }));
assert!(matches!(Cli::try_parse_from(["wai-quantum", "version", "--json"]).unwrap().command, Command::Version { json: true }));
let text = version_text(&mcp::versions());
assert!(text.starts_with(&format!("wai-quantum-cli {}\nengine wai-quantum {}\n", env!("CARGO_PKG_VERSION"), wai_quantum::engine::schema::ENGINE_VERSION)) && text.contains("wai.quantum.job-result/1") && text.contains(wai_quantum::engine::schema::POLICY), "{text}");
// The course, in either language.
match Cli::try_parse_from(["wai-quantum", "learn", "check", "bell", "b.qasm", "--lang", "zh-CN"]).unwrap() {
Cli { command: Command::Learn(a), lang, .. } => assert!(matches!(a.cmd, Some(learn::Cmd::Check { .. })) && lang.as_deref() == Some("zh-CN")),
_ => panic!("not learn"),
}
assert!(matches!(Cli::try_parse_from(["wai-quantum", "learn"]).unwrap().command, Command::Learn(learn::Args { cmd: None, .. })));
// The research tools take their own words.
match Cli::try_parse_from(["wai-quantum", "lab", "qec", "-d", "5", "--p", "0.003"]).unwrap().command {
Command::Lab { args } => assert_eq!(args, ["qec", "-d", "5", "--p", "0.003"]),
_ => panic!("not lab"),
}
}
}