Source code for uniqc.gateway.api.circuits

"""Circuit SVG rendering API — /api/circuits/{task_id}/svg."""

from __future__ import annotations

from html import escape
from typing import Any

from fastapi import APIRouter, HTTPException
from fastapi.responses import HTMLResponse

from uniqc.backend_adapter.task.store import TaskStore
from uniqc.gateway.db.archive_store import ArchiveStore

router = APIRouter()

_CIRCUIT_KEYS = {
    "source": (
        "circuit_ir",
        "source_circuit_ir",
        "submitted_circuit_ir",
        "originir",
        "origin_ir",
        "qasm",
        "qasm2",
        "openqasm",
        "circuit",
        "source_circuit",
        "submitted_circuit",
    ),
    "compiled": (
        "compiled_circuit_ir",
        "compiled_originir",
        "compiled_origin_ir",
        "compiled_qasm",
        "transpiled_qasm",
        "compiled_circuit",
        "transpiled_circuit",
    ),
    "executed": (
        "executed_circuit_ir",
        "execution_circuit_ir",
        "scheduled_circuit_ir",
        "executed_originir",
        "executed_qasm",
        "executed_circuit",
        "scheduled_circuit",
    ),
}


def _looks_like_circuit(value: Any) -> bool:
    if not isinstance(value, str):
        return False
    text = value.strip()
    if not text:
        return False
    upper = text.upper()
    return (
        upper.startswith("QINIT") or "OPENQASM" in upper or "QREG " in upper or "CREG " in upper or "MEASURE" in upper
    )


def _find_string_by_keys(data: Any, keys: tuple[str, ...]) -> str | None:
    if isinstance(data, dict):
        for key in keys:
            value = data.get(key)
            if _looks_like_circuit(value):
                return value
        for value in data.values():
            found = _find_string_by_keys(value, keys)
            if found:
                return found
    elif isinstance(data, list):
        for value in data:
            found = _find_string_by_keys(value, keys)
            if found:
                return found
    return None


def _detect_language(circuit_ir: str, fallback: str = "OriginIR") -> str:
    text = circuit_ir.lstrip().upper()
    if text.startswith("OPENQASM") or "QREG " in text:
        return "OpenQASM"
    return fallback


def _fallback_html(task_id: str, format: str, message: str, circuit_ir: str | None = None) -> HTMLResponse:
    pre = ""
    if circuit_ir:
        pre = f'<pre style="white-space:pre-wrap;margin:0;">{escape(circuit_ir)}</pre>'
    html = f"""
    <div style="font-family: system-ui, -apple-system, Segoe UI, sans-serif; color:#0f172a;">
      <div style="border:1px solid #cbd5e1;border-radius:8px;padding:14px;background:#f8fafc;">
        <strong>Task {escape(task_id)} · {escape(format)}</strong>
        <p style="margin:8px 0 12px;color:#475569;">{escape(message)}</p>
        {pre}
      </div>
    </div>
    """
    return HTMLResponse(content=html)


[docs] @router.get("/{task_id}/svg") def circuit_svg( task_id: str, format: str = "source", ) -> HTMLResponse: """Render a task's quantum circuit as an inline SVG HTML response. Query params: format: ``source`` (default) | ``compiled`` | ``executed`` - source: the originally submitted circuit IR - compiled: the compiled-to-basis circuit (if stored in metadata) - executed: the scheduled execution timeline (if stored) """ store = TaskStore() task = store.get(task_id) if task is None: task = ArchiveStore().get_archived(task_id) if task is None: raise HTTPException(status_code=404, detail=f"Task '{task_id}' not found") metadata = task.metadata or {} result = task.result or {} if format == "source": circuit_ir = ( _find_string_by_keys(metadata, _CIRCUIT_KEYS["source"]) or _find_string_by_keys(result, _CIRCUIT_KEYS["source"]) or "" ) language = metadata.get("circuit_language", "OriginIR") elif format == "compiled": circuit_ir = ( _find_string_by_keys(metadata, _CIRCUIT_KEYS["compiled"]) or _find_string_by_keys(result, _CIRCUIT_KEYS["compiled"]) or "" ) language = metadata.get("compiled_language", "OriginIR") if not circuit_ir: source = _find_string_by_keys(metadata, _CIRCUIT_KEYS["source"]) or _find_string_by_keys( result, _CIRCUIT_KEYS["source"] ) return _fallback_html( task_id, format, "Compiled circuit was not stored for this task. Showing source circuit when available.", source, ) elif format == "executed": circuit_ir = ( _find_string_by_keys(metadata, _CIRCUIT_KEYS["executed"]) or _find_string_by_keys(result, _CIRCUIT_KEYS["executed"]) or "" ) language = metadata.get("executed_language", "OriginIR") if not circuit_ir: # Fall back to compiled circuit_ir = ( _find_string_by_keys(metadata, _CIRCUIT_KEYS["compiled"]) or _find_string_by_keys(result, _CIRCUIT_KEYS["compiled"]) or _find_string_by_keys(metadata, _CIRCUIT_KEYS["source"]) or _find_string_by_keys(result, _CIRCUIT_KEYS["source"]) or "" ) language = metadata.get("compiled_language", "OriginIR") if not circuit_ir: return _fallback_html( task_id, format, "Executed/compiled circuit was not stored for this task.", ) else: raise HTTPException( status_code=400, detail="format must be one of: source, compiled, executed", ) if not circuit_ir: return _fallback_html( task_id, format, "No circuit metadata was stored for this task. Older tasks cannot be reconstructed from results alone; newly submitted tasks are captured automatically.", ) language = _detect_language(circuit_ir, fallback=str(language)) try: from uniqc.visualization.circuit import draw_html except ImportError as exc: html = f""" <div style="font-family: ui-monospace, SFMono-Regular, Menlo, monospace;"> <p style="color:#64748b;margin:0 0 12px;"> Circuit visualization dependency is not installed: {escape(str(exc))} </p> <pre style="white-space:pre-wrap;margin:0;">{escape(circuit_ir)}</pre> </div> """ return HTMLResponse(content=html) try: html = draw_html( circuit_ir, language=language, title=f"[{format.upper()}] {task_id}", ) return HTMLResponse(content=html) except Exception as exc: raise HTTPException( status_code=500, detail=f"Failed to render circuit: {exc}", ) from exc