Note
Go to the end to download the full example code.
Multi-photon transmon ladder spectroscopy
Two-tone spectroscopy at fixed frequency range but swept drive power, strong enough to drive multi-photon transitions up the transmon’s anharmonic ladder. An n-photon process reaching level n appears at the n-photon-averaged frequency
f_n = f01 - (n - 1) alpha / 2,
since climbing the ladder against a negative anharmonicity alpha costs
progressively less energy per photon. Each higher branch also needs
correspondingly higher drive power to appear at all – an n-photon transition
rate scales as the n-th power of the drive amplitude – so the ladder switches
on rung by rung as power rises rather than all at once, and each rung
power-broadens once driven hard enough to saturate.

Live figure — pick a tool, then zoom, pan, point-pick or annotate. Nothing is active until a tool is selected.
View this figure’s Vega export ↗ — the raw JSON spec, rendered live by a real Vega engine.
View this figure’s Vega-Lite export ↗ — the raw JSON spec(s), rendered live by a real Vega-Lite engine.
import numpy as np
import polars as pl
import plotpress
F01 = 5.050 # 0-1 transition, GHz
ANHARMONICITY = -0.220 # GHz (negative: transmon)
N_LEVELS = 4
frequency = np.linspace(F01 - 0.36, F01 + 0.01, 380)
power_dbm = np.linspace(-20.0, 15.0, 300)
F, P = np.meshgrid(frequency, power_dbm)
amplitude = 10.0 ** (P / 20.0) # linear drive amplitude from dBm
response = np.zeros_like(F)
for n in range(1, N_LEVELS + 1):
center = F01 + (n - 1) * ANHARMONICITY / 2.0
threshold = 0.55 + 0.65 * (n - 1) # higher rungs need more drive
turn_on = 1.0 / (1.0 + np.exp(-(np.log10(amplitude) - np.log10(threshold)) / 0.12))
width = 0.006 + 0.010 * amplitude / n
response += turn_on * width ** 2 / ((F - center) ** 2 + width ** 2)
# One row per swept (frequency, power) point -- sorted before the reshape
# below so the pivot back to a grid is correct regardless of row order.
sweep = pl.DataFrame({
"frequency_ghz": F.ravel(),
"power_dbm": P.ravel(),
"response": response.ravel(),
}).sort(["power_dbm", "frequency_ghz"])
frequency_axis = sweep["frequency_ghz"].unique().sort().to_numpy()
power_axis = sweep["power_dbm"].unique().sort().to_numpy()
response = sweep["response"].to_numpy().reshape(power_axis.size, frequency_axis.size)
fig, ax = plotpress.subplots(figsize=(7.6, 5.4))
mesh = ax.pcolormesh(frequency_axis, power_axis, response, cmap="plasma")
bar = fig.colorbar(mesh, ax=ax)
bar.set_title("response\n(a.u.)")
ax.set_xlabel("probe frequency (GHz)")
ax.set_ylabel("drive power (dBm)")
ax.set_title(f"Multi-photon ladder, anharmonicity = {ANHARMONICITY * 1e3:.0f} MHz")
fig.tight_layout()
Total running time of the script: (0 minutes 0.287 seconds)