Source code for uniqc.visualization.circuit_render.styles

"""Visual style definitions for circuit rendering.

Four skins over one semantic core (control dot / target circle, spanning
boxes for couplings, boxed gauge for measurement, double-line classical
wires).  ``quantikz`` is the default skin chosen in the design review.

A style is geometry + fonts + a per-family ``look`` (fill/stroke/ink) in two
themes (light/dark).  ``geom`` values feed both the layout engine and the
renderers so column widths always match what is drawn.
"""

from __future__ import annotations

from dataclasses import dataclass, field

__all__ = ["Style", "Theme", "Look", "STYLES", "STYLE_ORDER", "get_style"]

STYLE_ORDER = ("quantikz", "qiskit", "modern", "print")


[docs] @dataclass(frozen=True) class Look: fill: str stroke: str ink: str
[docs] @dataclass(frozen=True) class Theme: bg: str wire: str ink: str dim: str hint: str clane: str bracket: str
[docs] @dataclass(frozen=True) class Style: key: str font: str fs: float = 13.0 # gate label font size (px) pfs: float = 10.5 # parameter line font size wire_gap: float = 46.0 # distance between wire centers box_h: float = 34.0 min_col: float = 46.0 pad: float = 22.0 # horizontal padding inside a gate box ctrl_r: float = 4.2 # control-dot radius targ_r: float = 9.5 # CNOT target radius rx: float = 0.0 # box corner radius sw: float = 1.4 # stroke width wire_w: float = 1.4 themes: dict[str, Theme] = field(default_factory=dict) palette: dict[str, tuple[str, str]] = field(default_factory=dict) # family -> (fill, stroke), light palette_dark: dict[str, tuple[str, str]] | None = None
[docs] def theme(self, name: str) -> Theme: return self.themes.get(name) or self.themes["light"]
[docs] def look(self, theme: Theme, family: str) -> Look: """Box colors for a gate family (g1/grot/g2/measure/reset/channel/...).""" if not self.palette: return Look(fill=theme.bg, stroke=theme.wire, ink=theme.ink) pal = self.palette_dark if (theme is self.themes.get("dark") and self.palette_dark) else self.palette fill, stroke = pal.get(family, pal["g1"]) return Look(fill=fill, stroke=stroke, ink=theme.ink)
_SERIF = "Georgia, 'Times New Roman', serif" _SANS = "'Helvetica Neue', Arial, sans-serif" _MODERN = "'Inter', 'Segoe UI', system-ui, sans-serif" STYLES: dict[str, Style] = { "quantikz": Style( key="quantikz", font=_SERIF, fs=13.5, pfs=10.5, themes={ "light": Theme( bg="#ffffff", wire="#181b21", ink="#101319", dim="#5c6673", hint="#98a0ad", clane="#3c424d", bracket="#454c57", ), "dark": Theme( bg="#14171d", wire="#e9e7e2", ink="#ece9e2", dim="#9aa2b0", hint="#6e7684", clane="#d3d7de", bracket="#aab1bd", ), }, ), "qiskit": Style( key="qiskit", font=_SANS, fs=13.0, box_h=36.0, rx=2.0, ctrl_r=4.5, targ_r=10.0, themes={ "light": Theme( bg="#ffffff", wire="#3b4252", ink="#1a1f2b", dim="#5b6472", hint="#9aa2af", clane="#3c424d", bracket="#7c66d9", ), "dark": Theme( bg="#10141b", wire="#c7cedb", ink="#e6eaf2", dim="#9aa2b0", hint="#6e7684", clane="#c3c9d6", bracket="#a99af0", ), }, palette={ "g1": ("#dae8fc", "#6c8ebf"), "grot": ("#dae8fc", "#6c8ebf"), "g2": ("#ffe6cc", "#d79b00"), "measure": ("#f5f5f5", "#7f7f7f"), "reset": ("#e1d5e7", "#9673a6"), "channel": ("#f8cecc", "#b85450"), "qram": ("#d5e8d4", "#82b366"), "def": ("#fff2cc", "#d6b656"), "cop": ("#f5f5f5", "#8a8f99"), }, ), "modern": Style( key="modern", font=_MODERN, fs=13.0, box_h=36.0, wire_gap=48.0, min_col=50.0, pad=24.0, ctrl_r=5.0, targ_r=10.5, rx=9.0, sw=1.6, wire_w=1.5, themes={ "light": Theme( bg="#ffffff", wire="#94a3b8", ink="#0f172a", dim="#64748b", hint="#94a3b8", clane="#64748b", bracket="#8b5cf6", ), "dark": Theme( bg="#0b1220", wire="#3f4b63", ink="#e2e8f0", dim="#94a3b8", hint="#4b5771", clane="#8fa0bd", bracket="#a78bfa", ), }, palette={ "g1": ("#e0e7ff", "#6366f1"), "grot": ("#e0f2fe", "#0284c7"), "g2": ("#d1fae5", "#059669"), "measure": ("#fef9c3", "#ca8a04"), "reset": ("#fce7f3", "#db2777"), "channel": ("#fee2e2", "#dc2626"), "qram": ("#f3e8ff", "#9333ea"), "def": ("#cffafe", "#0891b2"), "cop": ("#f1f5f9", "#64748b"), }, palette_dark={ "g1": ("#312e81cc", "#818cf8"), "grot": ("#0c4a6ecc", "#38bdf8"), "measure": ("#713f12cc", "#fbbf24"), "reset": ("#831843cc", "#f472b6"), "channel": ("#7f1d1dcc", "#f87171"), "qram": ("#581c87cc", "#c084fc"), "def": ("#164e63cc", "#22d3ee"), "cop": ("#1e293bcc", "#94a3b8"), "g2": ("#064e3bcc", "#34d399"), }, ), "print": Style( key="print", font=_SANS, fs=12.0, pfs=10.0, wire_gap=42.0, box_h=30.0, min_col=42.0, pad=18.0, ctrl_r=4.0, targ_r=8.5, sw=1.2, wire_w=1.2, themes={ "light": Theme( bg="#ffffff", wire="#222222", ink="#111111", dim="#555555", hint="#999999", clane="#333333", bracket="#555555", ), "dark": Theme( bg="#191919", wire="#dddddd", ink="#eeeeee", dim="#aaaaaa", hint="#777777", clane="#cccccc", bracket="#aaaaaa", ), }, palette={ "g1": ("#ffffff", "#111111"), "grot": ("#efefef", "#111111"), "g2": ("#d4d4d4", "#111111"), "measure": ("#f7f7f7", "#333333"), "reset": ("#e3e3e3", "#111111"), "channel": ("hatch", "#111111"), "qram": ("#9a9a9a", "#111111"), "def": ("#ffffff", "#111111"), "cop": ("#ffffff", "#555555"), }, ), }
[docs] def get_style(key: str) -> Style: """Resolve a style key, raising a helpful error for unknown keys.""" try: return STYLES[key] except KeyError: raise ValueError(f"Unknown circuit-drawing style {key!r}. Available: {', '.join(STYLE_ORDER)}.") from None