"""Static HTML and SVG for a job, a receipt, a circuit and Bloch vectors.
What a notebook shows when no widget front end is there (a static viewer, a
converted page, a front end without the widget extension), and what the
widgets show before their script runs. Everything here is drawn from the
engine's answers; nothing is computed that the engine did not say. The
output is deterministic: the same answer writes the same bytes.
"""
from __future__ import annotations
import math
from html import escape
from typing import Any, Dict, List, Mapping, Optional, Sequence
GOOD = "#10b981"
BAD = "#ef4444"
BAR = "#7aa0ff"
_CARD = (
"font-family:ui-sans-serif,system-ui,-apple-system,'Segoe UI',sans-serif;font-size:13px;line-height:1.45;"
"border:1px solid var(--jp-border-color2,rgba(127,127,127,.35));border-radius:8px;padding:10px 12px;margin:2px 0;max-width:100%;overflow-x:auto"
)
_MUTED = "opacity:.7"
_MONO = "font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px"
#: The most outcomes a histogram lists.
TOP = 16
def _num(x: Any) -> str:
if isinstance(x, bool) or not isinstance(x, (int, float)):
return escape(str(x))
if isinstance(x, int):
return str(x)
if x == 0:
return "0"
return f"{x:.6g}"
def _badge(ok: bool, yes: str = "verified", no: str = "INVALID") -> str:
colour = GOOD if ok else BAD
return (
f'{escape(yes if ok else no)}'
)
def _short(s: Optional[str], keep: int = 10) -> str:
if not s:
return ""
return s if len(s) <= 2 * keep + 1 else f"{s[:keep]}…{s[-keep:]}"
def histogram_svg(counts: Mapping[str, int], shots: int, *, top: int = TOP, frequencies: bool = False) -> str:
"""Counts as horizontal bars, the most frequent first: the outcome, its
bar, its count (or its sampled frequency)."""
rows = list(counts.items())[:top]
if not rows:
return ""
label_w = max(7.5 * max(len(k) for k, _ in rows) + 12, 40)
bar_w = 260.0
row_h = 18
peak = max(v for _, v in rows) or 1
w = label_w + bar_w + 70
h = row_h * len(rows) + 4
out = [
f'")
return "".join(out)
def _table(head: Sequence[str], rows: List[Sequence[str]]) -> str:
th = "".join(f'
{escape(h)}
' for h in head)
body = "".join("
" + "".join(f'
{c}
' for c in r) + "
" for r in rows)
return f'
{th}
{body}
'
def result_html(data: Mapping[str, Any]) -> str:
"""A result's card from the Result widget's data: its method and accuracy
class, its counts as a histogram, its expectation values and amplitudes."""
parts = [f'
']
acc = data.get("accuracy") or "?"
parts.append(f'
{escape(str(data.get("method") or "?"))} · accuracy {escape(str(acc))}')
if data.get("accuracy_note"):
parts.append(f' ({escape(str(data["accuracy_note"]))})')
parts.append("
")
if data.get("why"):
parts.append(f'
{escape(str(data["why"]))}
')
counts = data.get("counts") or {}
if counts:
shots = int(data.get("shots") or 0)
listed = min(len(counts), TOP)
parts.append(f'
{shots} shots, {data.get("distinct_outcomes", len(counts))} distinct outcomes; the {listed} most frequent:
')
parts.append(histogram_svg(counts, shots))
rest = sum(list(counts.values())[TOP:]) + int(data.get("omitted_shots") or 0)
if rest:
parts.append(f'
{rest} more shots fell on outcomes not drawn
')
exps = data.get("expectations") or []
if exps:
rows = []
for e in exps:
err = ""
if e.get("bound") is not None:
err = f"± {_num(e['bound'])} (bound)"
elif e.get("stderr") is not None:
err = f"± {_num(e['stderr'])} (std. error)"
rows.append([escape(str(e.get("observable"))), _num(e.get("value")), err])
parts.append(_table(["observable", "value", "error"], rows))
amps = data.get("amplitudes") or []
if amps:
parts.append(_table(["basis state", "re", "im", "|a|²"], [[escape(str(a["bits"])), _num(a["re"]), _num(a["im"]), _num(a["re"] ** 2 + a["im"] ** 2)] for a in amps]))
if data.get("receipt"):
r = data["receipt"]
parts.append(f'
receipt {_badge(bool(r.get("valid")))} signed by {escape(_short(r.get("signer"), 14))}')
if r.get("demo"):
parts.append(f' (the published demo key: it shows the result is unaltered, not who signed it)')
parts.append("
")
parts.append("
")
return "".join(parts)
def receipt_html(data: Mapping[str, Any]) -> str:
"""A receipt's card from the Receipt widget's data."""
ok = bool(data.get("valid"))
rows = [
["signer", f'{escape(str(data.get("signer") or "—"))}'],
["key", escape(str(data.get("key") or "—"))],
["digest", escape(str(data.get("digest") or "—"))],
["signature", escape(_short(data.get("signature"), 16) or "—")],
["form", escape(f"{data.get('alg') or '?'} over {data.get('canon') or 'the first form'} canonical JSON")],
]
body = "".join(f'
{k}
{v}
' for k, v in rows)
note = ""
if data.get("demo"):
note = f'
Signed with the published demo key: it shows the result is unaltered, not who signed it.
'
what = escape(str(data.get("binds") or "the request and the result as given"))
return (
f'
receipt {_badge(v.get("valid") is True)} '
f'signed by {escape(str(v.get("signer") or v.get("key") or "?"))}
'
)
def circuit_html(data: Mapping[str, Any]) -> str:
"""The engine's drawing, in a card that scrolls when it is wide."""
return f'
{data["svg"]}
'
# -- Bloch vectors ----------------------------------------------------------
#: The view a drawing of Bloch vectors starts from, degrees: turned about z,
#: then tilted towards the reader.
VIEW = (-35.0, 20.0)
def project(v: Sequence[float], azimuth: float, elevation: float) -> tuple:
"""A point on or in the unit sphere as the reader sees it, in the
sphere's radii: (right, up). The same projection the widget draws."""
a, e = math.radians(azimuth), math.radians(elevation)
x, y, z = v
xr = x * math.cos(a) - y * math.sin(a)
yr = x * math.sin(a) + y * math.cos(a)
return (xr, z * math.cos(e) - yr * math.sin(e))
def bloch_svg(q: Mapping[str, Any], *, size: int = 150, view: Sequence[float] = VIEW) -> str:
"""One qubit's Bloch vector on its sphere, seen from ``view``."""
r = size * 0.36
cx, cy = size / 2, size / 2 + 4
az, el = view
def pt(v):
px, py = project(v, az, el)
return cx + r * px, cy - r * py
out = [
f'")
return "".join(out)
def bloch_html(data: Mapping[str, Any]) -> str:
"""Each qubit's sphere and vector, with its components and length."""
cells = []
for q in data.get("qubits") or []:
nums = f'x {_num(q["x"])} · y {_num(q["y"])} · z {_num(q["z"])} length {_num(q["length"])}'
cells.append(f'
{bloch_svg(q)}
{nums}
')
head = f'
Bloch vectors · {escape(str(data.get("method") or "?"))} · accuracy {escape(str(data.get("accuracy") or "?"))}
'
note = f'
Each qubit\'s reduced state: length 1 is pure, shorter is mixed (entangled with the others, or noisy).
'
return f'
{head}
{"".join(cells)}
{note}
'
def job_html(job: Any) -> str:
"""A job's card, as the Result widget's data draws it."""
from ._data import result_data
return result_html(result_data(job))