--- title: QCal Copilot emoji: ⚛️ colorFrom: blue colorTo: purple sdk: gradio sdk_version: 4.44.0 python_version: "3.12" app_file: app.py pinned: false license: mit short_description: AI-assisted quantum calibration + CUDA-Q + Ising decoder --- # QCal Copilot AI-assisted quantum calibration. Point it at a raw `.npy` trace (or image, or CSV) and it renders a plot, auto-fits the standard calibration model (Rabi, Ramsey, T1, T2-echo), hands both to NVIDIA's Ising Calibration VLM, and emits a ready-to-run CUDA-Q script seeded with the recommended tuning. Ships three ways: - **`pip install qcal-copilot`** — CLI + Python API (`qcal analyze`, `from qcal.data import from_npy`). - **Gradio web app** — `qcal serve` or [the hosted Space](https://huggingface.co/spaces/athurlow/qcal). - **Jupyter** — see `examples/` for Rabi, Ramsey-drift, and readout-IQ notebooks. ``` ┌─────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Upload │──▶│ Analyzer │──▶│ Code gen │──▶│ Simulator │ │ (img/csv│ │ (Ising VLM) │ │ (CUDA-Q) │ │ (cudaq.sample│ └─────────┘ └──────┬───────┘ └──────────────┘ └──────────────┘ │ ▼ ┌───────────────────────────────┐ │ Decoder (optional) │ │ Ising 3D CNN → PyMatching │ └───────────────────────────────┘ ``` ## Layout ``` app.py # Gradio UI + pipeline wiring pyproject.toml # package metadata + CLI entry point src/qcal/ data.py # image/CSV/.npy/.npz preprocessing + plot rendering fit.py # scipy-backed curve fits (Rabi/Ramsey/T1/T2) analyzer.py # Ising VLM (local HF or NIM) codegen.py # CUDA-Q script generator simulator.py # executes the generated script decoder.py # Ising 3D CNN pre-decoder + MWPM config.py # persists NIM API key to ~/.config/qcal/config.toml cli.py # `qcal ...` Typer commands examples/ # Rabi / Ramsey-drift / readout-IQ notebooks requirements.txt ``` The analyzer and simulator are decoupled, so adding a later-stage 3D CNN decoder or swapping in a different model is a one-file change. ## Install (pip) ```bash pip install qcal-copilot # CLI + NIM backend pip install "qcal-copilot[decoder]" # + PyMatching pip install "qcal-copilot[gui]" # + Gradio (for `qcal serve`) pip install "qcal-copilot[ml]" # + torch + transformers (local 35B VLM) pip install "qcal-copilot[all]" # everything ``` Store your NIM API key (or set `NVIDIA_API_KEY` in your shell): ```bash qcal login # prompts for the key, saves to ~/.config/qcal/config.toml (0600) ``` ### CLI ```bash # Rabi trace stored as a 1-D .npy with a matching time axis qcal analyze rabi.npy --experiment rabi --out report.md --script rabi.py # .npz archive with x, y arrays qcal analyze ramsey.npz --experiment ramsey --json out.json # Regenerate the CUDA-Q script from a saved analysis qcal generate out.json --out rabi.py # Run the Ising 3D CNN decoder on a synthetic syndrome volume qcal decode --variant fast --distance 5 --rounds 5 --p 0.005 --shots 128 # Launch the Gradio UI locally (needs [gui]) qcal serve ``` ### Python ```python from qcal.data import from_npy from qcal.analyzer import analyze_payload from qcal.codegen import generate_script payload = from_npy("rabi.npy", experiment_type="rabi", x_path="rabi_time.npy", x_unit="s") payload.fit # FitResult: {amplitude, freq_per_s, tau_s, offset, phase_rad, R^2} result = analyze_payload(payload, backend="auto") # "nim" if key present, else local print(result.markdown()) print(generate_script(result.parsed)) ``` Both `payload.fit` and `result` render as rich markdown in Jupyter. ## Quick start ### 1. System requirements - NVIDIA GPU (≥ 24 GB VRAM recommended for local 35B inference; otherwise use the NIM endpoint, which runs the model remotely). - CUDA 12.x drivers. - Python 3.10 – 3.12. ### 2. Install Python dependencies ```bash python -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install -r requirements.txt ``` ### 3. Install CUDA-Q CUDA-Q ships from NVIDIA as a standalone package. Pick one: ```bash # Option A — pip wheels (Linux x86_64, CUDA 12): pip install cudaq # Option B — NVIDIA container (recommended for reproducibility): docker pull nvcr.io/nvidia/nightly/cuda-quantum:latest ``` Confirm the install: ```bash python -c "import cudaq; print(cudaq.__version__)" ``` ### 4. Get access to the Ising Calibration model You have two options. **A. Hosted inference via NVIDIA NIM (easiest, no local download)** 1. Sign in at and request access to `nvidia/ising-calibration-1-35b-a3b`. 2. Create an API key. 3. Export it before starting the app: ```bash export NVIDIA_API_KEY="nvapi-…" ``` **B. Local Hugging Face weights (needs substantial VRAM)** 1. Request access to `nvidia/Ising-Calibration-1-35B-A3B` on Hugging Face. 2. Authenticate: ```bash huggingface-cli login ``` 3. The analyzer will download weights on first use. Override the model id with `QCAL_MODEL_ID` if you want to try a different checkpoint. ### 5. Run the app ```bash python app.py ``` Open . Upload a calibration plot, click **Analyze calibration**, inspect the generated CUDA-Q script, then click **Run simulation** to execute it on the `cudaq` simulator. ## Deploy to Hugging Face Spaces This repo is ready to deploy as a Gradio Space (e.g. `athurlow/qcal`). The YAML frontmatter at the top of this README tells Spaces which SDK to use and which file to run. 1. Push the repo to the Space: ```bash git remote add space https://huggingface.co/spaces/athurlow/qcal git push space claude/qcal-copilot-mvp-OZ9wj:main ``` 2. In the Space **Settings → Variables and secrets**, add: - `NVIDIA_API_KEY` — required; the hosted Space can't download the 35B VLM locally, so the app should call the NIM endpoint. 3. (Optional) Override model ids via Space secrets if you have custom deployments: `QCAL_NIM_MODEL`, `QCAL_DECODER_FAST_ID`, `QCAL_DECODER_ACCURATE_ID`. 4. **Hardware:** a free CPU Space runs the decoder's small CNN (~1.8M params) and the NIM-backed analyzer fine. A GPU Space (T4 or better) is only needed if you want to host the calibration VLM locally; `cudaq` requires an NVIDIA GPU Space to run the simulation stage. The app falls back gracefully when dependencies are missing: no `NVIDIA_API_KEY` → analyzer reports the missing key; no `cudaq` → simulator button surfaces the install hint; no `pymatching` → decoder shows density metrics without MWPM timing. ## Error-correction decoder (optional stage) After a successful calibration analysis, expand the **"Error-correction decoder (Ising 3D CNN)"** panel to run an NVIDIA Ising surface-code pre-decoder on a synthetic syndrome volume. The panel lets you: - Pick the `fast` (~912k params) or `accurate` (~1.79M params) variant. - Set code distance (d), syndrome rounds (T), physical error rate (p), and shot count. - See before/after metrics: syndrome density, CNN inference time, MWPM decode time on raw vs denoised syndromes (via PyMatching), and a logical-error-rate proxy. - View a side-by-side plot of the raw and denoised syndrome slices plus a before/after bar chart. The `p` slider auto-populates from the calibration analysis (larger drive amplitude mismatch → larger `p`). Running the decoder regenerates the CUDA-Q script with a header block documenting the decoder variant and improvement metrics. **What's synthetic and what's real:** syndromes are sampled from Bernoulli(p) with a few injected correlated chains, the matching graph is a toy 6-nearest- neighbor graph (not a stim-generated DEM), and "LER" is a syndrome-weight proxy. If the Ising decoder weights aren't reachable, the module falls back to a 3D neighbor-support sparsifier so the pipeline still demos end-to-end. **Swapping in real data:** call `qcal.decoder.run_decoder(...)` directly with your own `numpy` volume in place of the generated one, or replace `generate_syndromes()` with a stim-backed sampler. ## Environment variables | Variable | Purpose | | --------------------------- | ------------------------------------------------ | | `NVIDIA_API_KEY` | API key for the NIM endpoint (backend = `nim`) | | `QCAL_MODEL_ID` | Override local HF calibration VLM id | | `QCAL_NIM_MODEL` | Override NIM model name | | `QCAL_NIM_ENDPOINT` | Override NIM base URL | | `QCAL_DECODER_FAST_ID` | Override HF id for the fast decoder variant | | `QCAL_DECODER_ACCURATE_ID` | Override HF id for the accurate decoder variant | | `QCAL_HOST` | Gradio bind host (default `0.0.0.0`) | | `QCAL_PORT` | Gradio port (default `7860`) | | `QCAL_SHARE` | Set to `1` to enable Gradio public share link | | `QCAL_CONFIG_PATH` | Override config file path (default `~/.config/qcal/config.toml`) | | `NIM_API_KEY` | Alias for `NVIDIA_API_KEY` | ## Input formats - **`.npy` / `.npz`** (preferred for real-hardware workflows) — Raw measurement arrays from your control stack. Pass `--experiment` (or `experiment_type=`) so `qcal` knows the expected shape and fit model: | `experiment_type` | Array shape | Auto-fit model | |------------------------|---------------|----------------------------------------------| | `rabi` | `(N,)` | damped sine → `{amplitude, freq, tau, offset, phase}` | | `ramsey` | `(N,)` | damped cosine → `{amplitude, detuning, t2star, offset, phase}` | | `t1`, `t2_echo` | `(N,)` | exponential decay → `{amplitude, tau, offset}` | | `rabi_chevron` | `(F, A)` | heatmap (no fit) | | `readout_iq` | `(N, 2)` | scatter (no fit) | | `iq_trace`, `resonator_spec`, `unknown` | `(N,)` | plot only | `.npz` should contain at least a `y` key and optionally an `x` key. Disable fitting for air-gapped installs without `scipy` via `--no-fit` (CLI) or `from_npy(..., fit=False)` (Python). - **Images** — `.png`, `.jpg`, `.jpeg`, `.bmp`, `.tiff`, `.webp`. Any calibration artifact the VLM understands: Rabi chevrons, T1/T2 decays, Ramsey fringes, readout histograms, resonator spectroscopy, oscilloscope traces. - **CSV / TSV** — We render a preview image of the first 25 rows so the VLM can still reason about tabular sweeps. Large tables are truncated. ## Roadmap (post-MVP) - 3D CNN surface-code decoder stage wired in after analysis. - Persist analyses + scripts per session. - Real Rabi fit instead of the linear `suggested_theta` mapping. - Auth + multi-user deployment. - Regression-test golden plots against expected analyses. ## Development Module boundaries to keep the MVP clean: - `qcal.data` — file I/O and normalization only. - `qcal.analyzer` — model calls; returns a strict JSON dict. - `qcal.codegen` — pure function: analysis dict (+ optional decoder info) → script text. - `qcal.simulator` — executes script text; never imports `cudaq` itself. - `qcal.decoder` — Ising 3D CNN sparsifier + optional PyMatching MWPM. - `app.py` — UI glue only; no ML logic. ### Testing the decoder stage Without launching the UI: ```python from qcal.decoder import run_decoder r = run_decoder(variant="fast", distance=5, rounds=5, error_rate=0.01, n_shots=64) print(r.markdown()) print("density reduction:", round(r.density_reduction * 100, 1), "%") ``` Through the UI: 1. Upload any calibration plot and click **Analyze calibration**. 2. Expand **Error-correction decoder (Ising 3D CNN)**. 3. Pick `fast` or `accurate`, adjust `d` / `T` / `p`, click **Run decoder**. 4. Inspect the metrics panel (density reduction, MWPM timing, LER proxy improvement) and the side-by-side plot. 5. The CUDA-Q script auto-refreshes with a decoder header block.