Model quantum devices in Python.

Describe a device once. Then calculate its spectrum, fit it to your measurements and find better pulses.

Documentation
Two qubits, q₁ and q₂, coupled through a bus, each with a readout resonator and a drive line. The examples below model it.

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.

chip.py
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
Try

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.

fit.py
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
before fitting
Partmeasuredmodel off bybare
q15.30005.4 MHz5.3000
q25.42005.0 MHz5.4200
bus6.55005.3 MHz6.5500
r17.00001.4 MHz7.0000
r27.20001.5 MHz7.2000

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.

Photons at the device
—
qubit P₁ —
Tφ limit —
Readout error
—
integrated for
IQ plane● |0⟩● |1⟩
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.

pulse.py
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
|0⟩ → |1⟩ error
—
step 0ready
before optimization

Browser demo: a three-level model with finite-difference gradients. The Python example uses JAX.

Start modelling.

Build your first model