Transmon-Resonator Chain Emulation¶
This example simulates a qubit–resonator–qubit chain with coupled_transmon() (dipole coupling per coupled_transmon()).
We prepare \(|100\rangle\) (left transmon excited) and evolve for one resonant swap period \(T_{\mathrm{swap}} = \pi/(\sqrt{2}\,g)\). The same evolution is run twice:
Noiseless — unitary analog simulation (TDVP on the MPO).
Noisy — open-system simulation with relaxation and dephasing on the qubit sites (TJM trajectories).
PVM observables track probabilities for bitstrings using only local indices \(0\) and \(1\) per site. With qubit_dim = resonator_dim = 3, population in the \(|2\rangle\) level appears as leakage (not counted in those bitstrings).
1. Hamiltonian and initial state¶
1import numpy as np
2from mqt.yaqs import Hamiltonian, State
3
4length = 3 # qubit – resonator – qubit
5qubit_dim = 3
6resonator_dim = 3
7w_q = 4 / (2 * np.pi)
8w_r = 4 / (2 * np.pi)
9alpha = -0.3 / (2 * np.pi)
10g = 0.2 / (2 * np.pi)
11
12H_0 = Hamiltonian.coupled_transmon(
13 length=length,
14 qubit_dim=qubit_dim,
15 resonator_dim=resonator_dim,
16 qubit_freq=w_q,
17 resonator_freq=w_r,
18 anharmonicity=alpha,
19 coupling=g,
20)
21
22T_swap = np.pi / (np.sqrt(2) * g)
23dt = T_swap / 100
24
25# |100⟩: left qubit (site 0) in |1⟩
26state = State(
27 length,
28 initial="basis",
29 basis_string="100",
30 physical_dimensions=[qubit_dim, resonator_dim, qubit_dim],
31)
3. Noiseless SWAP¶
1import copy
2
3from mqt.yaqs import Simulator
4
5sim = Simulator(show_progress=False)
6result_clean = sim.run(copy.deepcopy(state), H_0, copy.deepcopy(sim_params))
1p100_clean = pvm_curve(result_clean, "100")
2p001_clean = pvm_curve(result_clean, "001")
3times = sim_params.times
4
5print(f"noiseless P(|001⟩) at T_swap = {p001_clean[-1]:.4f}")
6print(f"noiseless P(|100⟩) at T_swap = {p100_clean[-1]:.4f}")
7print(f"noiseless leakage at T_swap = {leakage_at_t(result_clean, -1):.4f}")
8
9assert p100_clean[-1] < 0.05 + 1e-9, "left qubit should be depopulated"
10assert p001_clean[-1] > 0.9 - 1e-9, "right qubit should receive excitation"
11assert leakage_at_t(result_clean, -1) < 0.05 + 1e-9
noiseless P(|001⟩) at T_swap = 0.9937
noiseless P(|100⟩) at T_swap = 0.0030
noiseless leakage at T_swap = 0.0023
4. Noisy SWAP¶
Relaxation and dephasing on transmon sites (even indices). Built-in lowering and pauli_z processes are 2×2; for qubit_dim = 3 we pass explicit jump matrices (Destroy and a computational-subspace dephasing operator). For Gaussian and other distributed noise strengths, see Realistic Noise Models.
1from mqt.yaqs import NoiseModel
2from mqt.yaqs.core.libraries.gate_library import Destroy
3
4relax = Destroy(qubit_dim).matrix
5dephase = np.diag([1.0, -1.0, 1.0]).astype(complex) # |2⟩ unaffected
6
7noise_model = NoiseModel(
8 [{"name": "t1", "sites": [i], "strength": 0.03, "matrix": relax} for i in (0, 2)]
9 + [{"name": "dephase", "sites": [i], "strength": 0.02, "matrix": dephase} for i in (0, 2)]
10)
11
12noisy_params = AnalogSimParams(
13 observables=[Observable(bstr) for bstr in all_bitstrings],
14 elapsed_time=T_swap,
15 dt=dt,
16 sample_timesteps=True,
17 num_traj=32,
18 random_seed=7,
19)
20
21result_noisy = sim.run(copy.deepcopy(state), H_0, noisy_params, noise_model)
1p100_noisy = pvm_curve(result_noisy, "100")
2p001_noisy = pvm_curve(result_noisy, "001")
3
4print(f"noisy P(|001⟩) at T_swap = {p001_noisy[-1]:.4f}")
5print(f"noisy P(|100⟩) at T_swap = {p100_noisy[-1]:.4f}")
6print(f"noisy leakage at T_swap = {leakage_at_t(result_noisy, -1):.4f}")
7
8assert p001_noisy[-1] < p001_clean[-1] - 0.02, "noise should reduce swap fidelity"
noisy P(|001⟩) at T_swap = 0.0999
noisy P(|100⟩) at T_swap = 0.0572
noisy leakage at T_swap = 0.0136
5. Comparison plot¶
1import matplotlib.pyplot as plt
2
3fig, (ax_pop, ax_leak) = plt.subplots(1, 2, figsize=(9, 3.5))
4
5ax_pop.plot(times, p001_clean, "-", color="tab:blue", label=r"noiseless $P(|001\rangle)$")
6ax_pop.plot(times, p100_clean, "-", color="tab:orange", label=r"noiseless $P(|100\rangle)$")
7ax_pop.plot(times, p001_noisy, "--", color="tab:blue", label=r"noisy $P(|001\rangle)$")
8ax_pop.plot(times, p100_noisy, "--", color="tab:orange", label=r"noisy $P(|100\rangle)$")
9ax_pop.axvline(T_swap, color="gray", linestyle=":", alpha=0.6, label=r"$T_{\mathrm{swap}}$")
10ax_pop.set_xlabel("time")
11ax_pop.set_ylabel("probability")
12ax_pop.set_title("SWAP populations: noiseless vs noisy")
13ax_pop.legend(fontsize=8)
14ax_pop.grid(alpha=0.3)
15
16leak_clean = [leakage_at_t(result_clean, i) for i in range(len(times))]
17leak_noisy = [leakage_at_t(result_noisy, i) for i in range(len(times))]
18ax_leak.plot(times, leak_clean, "-", color="tab:green", label="noiseless leakage")
19ax_leak.plot(times, leak_noisy, "--", color="tab:red", label="noisy leakage")
20ax_leak.set_xlabel("time")
21ax_leak.set_ylabel("leakage")
22ax_leak.set_title("Population outside 0/1 subspace per site")
23ax_leak.legend(fontsize=8)
24ax_leak.grid(alpha=0.3)
25
26plt.tight_layout()
27plt.show()