Model quantum devices in Python.
Describe a device once. Then calculate its spectrum, fit it to your measurements and find better pulses.
Calculate spectra.
Here, the ZZ shift between two qubits as q₂ is tuned. Drag any red number in the code and the curve is recomputed in your browser.
from quchip import Chip, DuffingTransmon, Resonator, Capacitive q1 = DuffingTransmon(freq=5.326, anharmonicity=-0.261, levels=3) q2 = DuffingTransmon(freq=5.192, anharmonicity=-0.263, levels=3) bus = Resonator(freq=6.300, levels=3) chip = Chip([q1, q2, bus], couplings=[ Capacitive(q1, bus, g=0.030), Capacitive(q2, bus, g=0.030), ]) chip.freq(q1) # 5.325003 GHz chip.freq(q1, when={q2: 1}) - chip.freq(q1) # ζ = +14.43 kHzfrom quchip import (Chip, Resonator, DuffingTransmon, Capacitive) q1 = DuffingTransmon(freq=5.326, anharmonicity=-0.261, levels=3) q2 = DuffingTransmon(freq=5.192, anharmonicity=-0.263, levels=3) bus = Resonator(freq=6.300, levels=3) chip = Chip([q1, q2, bus], couplings=[ Capacitive(q1, bus, g=0.030), Capacitive(q2, bus, g=0.030)]) chip.freq(q1) # 5.325003 GHz (chip.freq(q1, when={q2: 1}) - chip.freq(q1)) # ζ = +14.43 kHz
Exact diagonalization, 27 states
Fit what you measured.
Describe the device as you measured it. quchip finds the bare parameters that reproduce every measured frequency and shift.
from quchip import Chip, DuffingTransmon, Resonator, Capacitive from quchip import fit_a_dress # the device as measured, in GHz q1 = DuffingTransmon(freq=5.300, anharmonicity=-0.300, levels=3) q2 = DuffingTransmon(freq=5.420, anharmonicity=-0.300, levels=3) bus = Resonator(freq=6.550, levels=4) r1 = Resonator(freq=7.000, levels=3) r2 = Resonator(freq=7.200, levels=3) # on a measured chip, g is the shift each pair shows measured = Chip([q1, q2, bus, r1, r2], couplings=[ Capacitive(q1, bus, g=-0.0012), Capacitive(q2, bus, g=-0.0012), Capacitive(q1, r1, g=-0.0005), Capacitive(q2, r2, g=-0.0005), ]) fit = fit_a_dress(measured) fit.chip # the bare device that reproduces all 11 numbersfrom quchip import (Chip, DuffingTransmon, Resonator, Capacitive, fit_a_dress) # the device as measured, in GHz q1 = DuffingTransmon(freq=5.300, anharmonicity=-0.300, levels=3) q2 = DuffingTransmon(freq=5.420, anharmonicity=-0.300, levels=3) bus = Resonator(freq=6.550, levels=4) r1 = Resonator(freq=7.000, levels=3) r2 = Resonator(freq=7.200, levels=3) # on a measured chip, g is the # shift each pair shows measured = Chip([q1, q2, bus, r1, r2], couplings=[ Capacitive(q1, bus, g=-0.0012), Capacitive(q2, bus, g=-0.0012), Capacitive(q1, r1, g=-0.0005), Capacitive(q2, r2, g=-0.0005)]) fit = fit_a_dress(measured) fit.chip # the bare device
| Part | measured | model off by | bare |
|---|---|---|---|
| q1 | 5.3000 | 5.4 MHz | 5.3000 |
| q2 | 5.4200 | 5.0 MHz | 5.4200 |
| bus | 6.5500 | 5.3 MHz | 6.5500 |
| r1 | 7.0000 | 1.4 MHz | 7.0000 |
| r2 | 7.2000 | 1.5 MHz | 7.2000 |
| 1 Hz10 MHz |
Frequencies in GHz. Fitted together with two anharmonicities and four cross-Kerr shifts: 11 measurements, 11 parameters. Bars are logarithmic, from 1 Hz to 10 MHz.
Include the wiring.
See how attenuation, temperature and amplifier noise reach the qubit and set its readout error.
Tφ limit —
About this model
Both input lines use the same attenuation stages, with thermal occupation n → ηn + (1−η)n̄(T). Control-line noise sets the qubit’s excited population; probe-line noise adds dephasing. Amplifier noise broadens the IQ clouds. Dot colours show each component’s share of the output noise.
Qubit: 5.3 GHz, intrinsic T₁ = 100 µs, control-line T₁ = 1 ms. Resonator: 6.5 GHz, κ/2π = 2 MHz, χ/2π = 1 MHz. Readout fields differ by 0.2 √(photons/ns). Dephasing follows Clerk & Utami (2007); output noise follows the Friis cascade.
Find a better pulse.
Gradients flow through the whole simulation, so an optimizer can tune the pulse and cut the gate error.
q = DuffingTransmon(freq=5.0, anharmonicity=-0.26, levels=3, label="q") chip = Chip([q], frame="rotating", approximation=RWA(), backend="dynamiqs") xy = ChargeDrive(q, label="xy"); chip.wire(xy) seq = QuantumSequence(chip) seq.schedule(xy, freq=5.0, envelope=GaussianDRAG(duration=6)) def error(p): run = seq.with_params(p).simulate(tlist=t, initial_state={"q": 0}) return 1 - run.population("q", level=1)[-1] p = {"pulse.0.amplitude": 0.2000, "pulse.0.beta": 0.0000, "pulse.0.sigmas": 3.000, "pulse.0.freq": 5.00000} grad = jax.grad(error)(p) # step 0q = DuffingTransmon(freq=5.0, levels=3, anharmonicity=-0.26, label="q") chip = Chip([q], frame="rotating", approximation=RWA(), backend="dynamiqs") xy = ChargeDrive(q, label="xy") chip.wire(xy) seq = QuantumSequence(chip) seq.schedule(xy, freq=5.0, envelope= GaussianDRAG(duration=6)) def error(p): sim = seq.with_params(p) run = sim.simulate(tlist=t, initial_state={"q": 0}) p1 = run.population("q", level=1) return 1 - p1[-1] p = {"pulse.0.amplitude": 0.2000, "pulse.0.beta": 0.0000, "pulse.0.sigmas": 3.000, "pulse.0.freq": 5.00000} grad = jax.grad(error)(p) # step 0
Browser demo: a three-level model with finite-difference gradients. The Python example uses JAX.