open-standards/wai/wai-quantum-cli/smoke.sh
Transaction Science 605ea19aa2 wai-quantum-cli: the command line on the job service's engine, installable
A new crate, wai-quantum-cli, whose binary is wai-quantum. It depends on the
wai-quantum library alone, so `cargo install wai-quantum-cli` can install it.

- run: an OpenQASM program planned and run as the service runs it
  (wai_quantum::engine), signed with this user's key; --json prints the job
  as the service returns it. Shots, seed, method, observables, amplitudes,
  noise and method options; an unknown flag or noise channel is an error.
- plan, lint (the line of the first problem), draw (the circuit as text,
  gates packed into columns, conditions in a legend).
- submit: the same job on the service (feature `service`, on by default),
  waiting for a long one.
- verify: a job's receipt from run, submit or the service, and every
  receipt the research tools sign; a changed result is INVALID.
- key: this user's did:key; --key FILE and --demo-key as before.
- methods: each method's reach in a job's memory here.
- mcp: run, plan, lint, draw, verify and the method list for an agent,
  over stdio; runs stay on this machine.
- lab: the earlier wai-quantum command, moved here from wai-rs unchanged
  (wai:: paths now wai_quantum::), its key handling shared with run.
- completions for bash, zsh, fish, elvish and PowerShell.
- Feature `metal`: the GPU state vector on macOS. With no default features
  it builds for wasm32-wasip2.

wai-rs loses the wai-quantum bin and its cli_keys feature. The wai README's
quickstart now uses the new crate. smoke.sh exercises every command, and
.github/workflows/wai-quantum-cli.yml runs the tests, clippy with and
without submit, smoke.sh, and the WASI component against the native build,
byte for byte; on macOS also a run on the GPU.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit d2459835d8)
2026-10-20 17:34:16 -04:00

111 lines
4.6 KiB
Shell

#!/usr/bin/env bash
# smoke.sh: the command line as a reader uses it, every check exact.
#
# bash smoke.sh path/to/wai-quantum [SERVICE_URL]
#
# With SERVICE_URL, `submit` sends one job to the service there and its
# receipt is checked here. Each command's output is written to a file before
# it is read, so no check depends on a pipe closing early.
set -euo pipefail
W="$1"
SVC="${2:-}"
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
export XDG_CONFIG_HOME="$tmp/config"
unset WAI_QUANTUM_KEY
fail() { echo "smoke: $*" >&2; exit 1; }
has() { grep -q -- "$2" "$1" || { echo "--- $1:" >&2; cat "$1" >&2; fail "expected: $2"; }; }
cat > "$tmp/ghz.qasm" <<'EOF'
OPENQASM 3.0;
include "stdgates.inc";
qubit[3] q;
bit[3] c;
h q[0];
cx q[0], q[1];
cx q[1], q[2];
c = measure q;
EOF
"$W" lint "$tmp/ghz.qasm" > "$tmp/lint"
has "$tmp/lint" "ok — 3 qubits, 3 gates, 3 bits measured"
printf 'OPENQASM 3.0;\nqubit[1] q;\nfoo q[0];\n' > "$tmp/bad.qasm"
if "$W" lint "$tmp/bad.qasm" > "$tmp/lint-bad"; then fail "lint passed a program with an unknown gate"; fi
has "$tmp/lint-bad" "bad.qasm:3: unknown gate foo"
echo "lint: ok"
"$W" plan "$tmp/ghz.qasm" --json > "$tmp/plan.json"
python3 -c 'import json,sys; p=json.load(open(sys.argv[1])); assert p["method"]=="stabilizer" and p["qubits"]==3, p' "$tmp/plan.json"
echo "plan: ok, stabilizer"
"$W" run "$tmp/ghz.qasm" --shots 1000 -o "Z0 Z2" --json > "$tmp/job.json" 2> "$tmp/run.err"
has "$tmp/run.err" "made a signing key"
python3 - "$tmp/job.json" <<'EOF'
import json, sys
j = json.load(open(sys.argv[1]))
r = j["result"]
assert j["status"] == "done" and j["method"] == "stabilizer", j
assert set(r["counts"]) <= {"000", "111"} and sum(r["counts"].values()) == 1000, r["counts"]
assert r["expectations"][0]["value"] == 1.0, r["expectations"]
assert r["receipt"]["alg"] == "ed25519" and len(r["receipt"]["key"]) == 64, r["receipt"]
assert j["request"] == {"qasm": j["request"]["qasm"], "shots": 1000, "observables": ["Z0 Z2"]}, j["request"]
EOF
"$W" verify "$tmp/job.json" > "$tmp/verify"
has "$tmp/verify" "^signature VALID"
python3 -c 'import json,sys; j=json.load(open(sys.argv[1])); j["result"]["counts"]={"000":1000}; json.dump(j,open(sys.argv[2],"w"))' "$tmp/job.json" "$tmp/forged.json"
if "$W" verify "$tmp/forged.json" > "$tmp/verify-forged"; then fail "a changed result verified"; fi
has "$tmp/verify-forged" "^signature INVALID"
# The same user, the same key; the signer is the key's did:key.
"$W" key > "$tmp/key" 2> /dev/null
has "$tmp/key" "^did:key:z6Mk"
has "$tmp/verify" "^signer $(cat "$tmp/key")"
echo "run, verify, key: ok, signed by $(cat "$tmp/key")"
"$W" run "$tmp/ghz.qasm" > "$tmp/run-text"
has "$tmp/run-text" "^method stabilizer"
has "$tmp/run-text" "^receipt verifies"
if "$W" run "$tmp/ghz.qasm" --shot 5 > /dev/null 2> "$tmp/typo"; then fail "an unknown flag was accepted"; fi
has "$tmp/typo" "unexpected argument '--shot'"
if "$W" run "$tmp/ghz.qasm" --noise depolarizing=0.1 > /dev/null 2> "$tmp/noise"; then fail "an unknown noise channel was accepted"; fi
has "$tmp/noise" "no such channel"
echo "refusals: ok"
"$W" draw "$tmp/ghz.qasm" > "$tmp/draw"
has "$tmp/draw" "^q0: .*H.*●"
has "$tmp/draw" "^q2: .*⊕.*M2"
echo "draw: ok"
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
'{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"run_quantum_circuit","arguments":{"qasm":"OPENQASM 3.0; qubit[2] q; h q[0]; cx q[0], q[1];","observables":["Z0 Z1"]}}}' \
> "$tmp/mcp.in"
"$W" mcp < "$tmp/mcp.in" > "$tmp/mcp.out" 2> /dev/null
python3 - "$tmp/mcp.out" <<'EOF'
import json, sys
lines = [json.loads(l) for l in open(sys.argv[1])]
assert [m["id"] for m in lines] == [1, 2, 3], lines
assert lines[0]["result"]["serverInfo"]["name"] == "wai-quantum"
assert "run_quantum_circuit" in [t["name"] for t in lines[1]["result"]["tools"]]
job = lines[2]["result"]["structuredContent"]
assert job["result"]["expectations"][0]["value"] == 1.0, job
EOF
echo "mcp: ok"
"$W" lab list > "$tmp/lab-list"
has "$tmp/lab-list" "^ghz "
"$W" lab receipt ghz -n 3 > "$tmp/lab-receipt" 2> /dev/null
"$W" verify "$tmp/lab-receipt" > "$tmp/lab-verify"
has "$tmp/lab-verify" "^signature VALID"
has "$tmp/lab-verify" "^signer $(cat "$tmp/key")"
echo "lab: ok"
if [ -n "$SVC" ]; then
"$W" submit "$tmp/ghz.qasm" --shots 500 --service "$SVC" --json > "$tmp/svc.json"
"$W" verify "$tmp/svc.json" > "$tmp/svc-verify"
has "$tmp/svc-verify" "^signature VALID"
echo "submit: ok, the service's receipt verifies here"
fi
echo "smoke: every check held"