open-standards/wai/wai-quantum-py/python/wai_quantum/_data.py
Transaction Science 22fdc7a3cb wai-quantum-py: WAI Quantum in Python and notebooks, in-process
A new crate, wai/wai-quantum-py, built by maturin 1.15.0 into one abi3 wheel
per platform (pyo3 0.29.3, Python 3.9 on), module wai_quantum. Its extension
module wraps the shared job engine and nothing else: run, plan, lint, draw
(text and the engine SVG), layout, critique, improve, shortcuts,
equivalent, verify, seal, canonical, digest, methods, accuracy_classes and
history, values crossing one to one with JSON (NaN, integers past 64 bits
and non-string keys refused). A job is the service form as a plain dict
with helpers (counts, probabilities, expectations, amplitudes, method,
accuracy, receipt); Key reads and writes the command line key files and
finds the same user key; bloch gives each qubit reduced state from the
engine expectation values. A refusal raises EngineError with the engine
message, its line, and a code slot for the engine diagnostic codes.

Service is a client in the standard library alone for the job service
(submit, status, result, cancel, run), naming itself
wai-quantum-python/<version>, its receipts checked here offline.

Widgets (the widgets extra): Circuit (the engine drawing, gates clickable,
authorship marks from a history), Result, Receipt (the engine verdict) and
Bloch (drag to turn), each a plain ES module, each with static HTML (and
SVG) fallbacks that render without the widget library.

Five doc notebooks run in CI and are held to their stored outputs.
ci/check.sh is the whole check; 62 pytest tests hold the package to the
command line (same results, plans, lints, critiques, drawings, methods,
equivalence verdicts; receipts verified both ways; the same user key; the
digest recomputed from Python canonical bytes), to the service run locally,
and run the front ends under node on the payloads; Python 3.9 smoke with
the wheel alone. Workflow wai-quantum-py.yml runs it on linux and macos.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 19:51:08 -04:00

116 lines
4.3 KiB
Python

"""The data each widget draws: plain values built from the engine's answers.
Kept apart from the widgets so that a job's static card (``Job._repr_html_``)
needs no widget library.
"""
from __future__ import annotations
from typing import Any, Dict, List, Mapping, Optional, Sequence, Union
from . import _api, _engine
from ._job import Job
from .keys import DEMO_SEED, Key
__all__ = ["circuit_data", "result_data", "receipt_data", "bloch_data"]
_DEMO_KEY = Key(DEMO_SEED).public_key
def circuit_data(program: _api.Program, *, hands: Optional[Sequence[str]] = None, history: Optional[Mapping[str, Any]] = None, version: Optional[str] = None) -> Dict[str, Any]:
"""A circuit's drawing: the engine's SVG, its text drawing and layout,
and, when known, whose hand each gate is in (``hands``, one per
operation, or read from a program ``history`` at ``version``) with the
key's counts."""
src = _api._source(program)
if history is not None:
if hands is not None:
raise ValueError("give hands or a history, not both")
blame = _api.history("blame", {"history": history, "version": version or _head(history)})
if blame.get("unread"):
raise ValueError(f"the history's version does not read: {blame['unread']}")
hands = blame.get("gates")
hands = list(hands) if hands is not None else None
key: List[Dict[str, Any]] = []
if hands:
for name, label in _engine.HANDS:
n = sum(1 for h in hands if h == name)
if n:
key.append({"hand": name, "label": label, "gates": n})
return {
"svg": _api.draw(src, format="svg", hands=hands),
"text": _api.draw(src),
"layout": _api.layout(src),
"hands": hands,
"key": key,
}
def _head(history: Mapping[str, Any]) -> str:
log = _api.history("log", {"history": history})
heads = [v["id"] for v in log.get("versions", []) if v.get("head")]
if len(heads) != 1:
raise ValueError(f"the history has {len(heads)} heads: give version=")
return heads[0]
def _seal_check(job: Mapping[str, Any]) -> Dict[str, Any]:
v = _api.verify(job)
r = (job.get("result") or {}).get("receipt") or {}
return {
"valid": v.valid,
"signer": v.signer,
"key": v.key,
"digest": r.get("digest"),
"signature": r.get("signature"),
"alg": r.get("alg"),
"canon": r.get("canon"),
"demo": v.key == _DEMO_KEY,
"binds": "the request and the result as given",
}
def result_data(job: Mapping[str, Any]) -> Dict[str, Any]:
"""A result's histogram and figures: its counts (the most frequent
first), the shots, its expectation values and amplitudes, the method, the
accuracy class and what it says, and the receipt's check when it has
one."""
j = job if isinstance(job, Job) else Job(job)
r = j.result
acc = r.get("accuracy")
note = None
if isinstance(acc, dict):
note = acc.get("note")
data: Dict[str, Any] = {
"method": j.method,
"accuracy": j.accuracy,
"accuracy_note": note,
"why": j.get("why"),
"counts": j.counts,
"shots": j.shots,
"distinct_outcomes": r.get("distinct_outcomes", len(j.counts)),
"omitted_shots": r.get("omitted_shots", 0),
"expectations": r.get("expectations") or [],
"amplitudes": r.get("amplitudes") or [],
"receipt": _seal_check(j) if j.receipt else None,
}
return data
def receipt_data(job: Mapping[str, Any]) -> Dict[str, Any]:
"""A receipt as the reader is shown it: the signer, the key, the digest
and the signature, and whether it seals the request and the result,
checked here by the engine."""
if not (job.get("result") or {}).get("receipt"):
raise ValueError("the result carries no receipt")
return _seal_check(job)
def bloch_data(program: Union[_api.Program, Mapping[str, Any]], **kw: Any) -> Dict[str, Any]:
"""Each qubit's Bloch vector from the engine (``wai_quantum.bloch``), or
from what ``bloch`` returned. The job itself is kept out of the data."""
if isinstance(program, Mapping) and "qubits" in program and "qasm" not in program:
b = program
else:
b = _api.bloch(program, **kw)
return {"qubits": [dict(q) for q in b["qubits"]], "method": b["method"], "accuracy": b["accuracy"]}