File size: 12,847 Bytes
55a628f 2fb5b49 55a628f d7214cb ed74525 d7214cb ed74525 0e83549 ed74525 d7214cb ed74525 0e83549 d7214cb ed74525 d7214cb ed74525 55a628f 0e83549 ed74525 0e83549 d7214cb ed74525 d7214cb ed74525 0e83549 ed74525 0e83549 ed74525 0e83549 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | ---
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 <https://build.nvidia.com/> 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 <http://localhost:7860>. 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.
|