Realistic Noise Models¶
YAQS ships a library of physically motivated jump operators—relaxation (lowering), excitation (raising), single-qubit Pauli channels, and nearest-neighbor crosstalk (crosstalk_xx, crosstalk_zz, …)—that you assemble into a NoiseModel.
For hardware with static disorder (calibration drift, fabrication spread), each process strength can be a distribution instead of a fixed float. YAQS samples one concrete strength per process when run() starts; all trajectories in that run share the same sampled disorder. The realized model is stored on noise_model.
This page shows:
A typical multi-channel noise model for an analog chain.
Gaussian (bell-curve) strengths and the other built-in distributions.
How sampled disorder changes open-system dynamics compared to a mean-strength baseline.
1. Built-in noise processes¶
Each process is a dictionary with name, sites, and strength. YAQS fills in the operator matrix (or per-site factors for long-range crosstalk) from NoiseLibrary.
1from mqt.yaqs import NoiseModel
2
3L = 4
4processes = [
5 {"name": "lowering", "sites": [i], "strength": 0.05} for i in range(L)
6] + [
7 {"name": "pauli_z", "sites": [i], "strength": 0.02} for i in range(L)
8] + [
9 {"name": "crosstalk_xx", "sites": [i, i + 1], "strength": 0.01} for i in range(L - 1)
10]
11
12noise_model = NoiseModel(processes)
13print(f"{len(noise_model.processes)} processes:")
14for proc in noise_model.processes:
15 print(f" {proc['name']:16s} sites={proc['sites']} strength={proc['strength']}")
11 processes:
lowering sites=[0] strength=0.05
lowering sites=[1] strength=0.05
lowering sites=[2] strength=0.05
lowering sites=[3] strength=0.05
pauli_z sites=[0] strength=0.02
pauli_z sites=[1] strength=0.02
pauli_z sites=[2] strength=0.02
pauli_z sites=[3] strength=0.02
crosstalk_xx sites=[0, 1] strength=0.01
crosstalk_xx sites=[1, 2] strength=0.01
crosstalk_xx sites=[2, 3] strength=0.01
2. Bell-curve (Gaussian) disorder on strengths¶
Replace a scalar strength with a dict describing the distribution. For a normal (Gaussian) bell curve, set distribution to "normal" and provide mean and std:
1bell_curve_strength = {"distribution": "normal", "mean": 0.08, "std": 0.02}
2
3disordered_processes = [
4 {
5 "name": "pauli_z",
6 "sites": [i],
7 "strength": bell_curve_strength,
8 }
9 for i in range(L)
10]
11
12disordered_model = NoiseModel(disordered_processes)
Other supported distributions:
|
Parameters |
Use when |
|---|---|---|
|
|
Symmetric spread around a target rate; negatives are clamped to |
|
|
Same shape as normal but sampled only for non-negative strengths. |
|
|
Log-normal rates (strictly positive; |
Sample many independent disorder realizations and plot the bell curve:
1import matplotlib.pyplot as plt
2import numpy as np
3
4rng = np.random.default_rng(0)
5samples = [disordered_model.sample(rng=rng).processes[0]["strength"] for _ in range(5000)]
6
7fig, ax = plt.subplots(figsize=(6, 3.5))
8ax.hist(samples, bins=40, density=True, alpha=0.7, color="tab:blue", label="sampled strengths")
9x = np.linspace(0, max(samples), 200)
10pdf = (
11 1.0 / (bell_curve_strength["std"] * np.sqrt(2 * np.pi))
12 * np.exp(-0.5 * ((x - bell_curve_strength["mean"]) / bell_curve_strength["std"]) ** 2)
13)
14ax.plot(x, pdf, color="black", lw=1.5, label="Gaussian pdf (negatives clamped)")
15ax.set_xlabel("sampled dephasing strength")
16ax.set_ylabel("density")
17ax.set_title("Bell-curve disorder from a normal strength distribution")
18ax.legend()
19ax.grid(alpha=0.3)
20plt.tight_layout()
21plt.show()
Sampled noise strength -0.000463 using 'normal' distribution (mean=0.080000, std=0.020000) was negative and clamped to 0.0.
3. Disorder in an analog simulation¶
We evolve a short Ising chain and compare:
Baseline: every site uses the distribution mean (
0.08) as a fixed strength.Disordered: strengths are drawn from the bell curve once at the start of each run.
1from mqt.yaqs import AnalogSimParams, Hamiltonian, Observable, Simulator, State
2
3hamiltonian = Hamiltonian.ising(length=L, J=1.0, g=0.5)
4state = State(L, initial="zeros")
5z_obs = Observable("z", sites=0)
6
7sim_params = AnalogSimParams(
8 observables=[z_obs],
9 elapsed_time=2.0,
10 dt=0.1,
11 num_traj=64,
12 max_bond_dim=16,
13 random_seed=7,
14)
15
16baseline_model = NoiseModel([
17 {"name": "pauli_z", "sites": [i], "strength": bell_curve_strength["mean"]} for i in range(L)
18])
19
20sim = Simulator(show_progress=False)
21result_baseline = sim.run(state, hamiltonian, sim_params, baseline_model)
22result_disordered = sim.run(state, hamiltonian, sim_params, disordered_model)
23
24print("Sampled strengths from the disordered run:")
25for proc in result_disordered.noise_model.processes:
26 print(f" site {proc['sites'][0]}: {proc['strength']:.4f}")
Sampled strengths from the disordered run:
site 0: 0.0800
site 1: 0.0860
site 2: 0.0745
site 3: 0.0622
1times = sim_params.times
2baseline_curve = result_baseline.expectation_values[0]
3disordered_curve = result_disordered.expectation_values[0]
4
5plt.figure(figsize=(7, 4))
6plt.plot(times, baseline_curve, label="fixed mean strength", color="black", linestyle="--")
7plt.plot(times, disordered_curve, label="bell-curve disorder (one sample / run)", color="tab:orange")
8plt.xlabel("time")
9plt.ylabel(r"$\langle Z_0 \rangle$")
10plt.title("Static disorder shifts open-system decay")
11plt.legend()
12plt.grid(alpha=0.3)
13plt.tight_layout()
14plt.show()
Re-running with the same random_seed reproduces the same sampled strengths and trajectory-averaged curve. Leave random_seed=None for fresh disorder draws in production Monte Carlo studies.
4. Disorder on a noisy circuit¶
The same distribution syntax works in digital simulation. Below, bit-flip rates on each qubit follow independent bell curves; one sample is drawn per Simulator.run call.
1from mqt.yaqs import Observable, StrongSimParams
2from mqt.yaqs.core.libraries.circuit_library import create_ising_circuit
3
4num_qubits = 3
5circuit = create_ising_circuit(L=num_qubits, J=1.0, g=0.5, dt=0.1, timesteps=5)
6
7circuit_noise = NoiseModel([
8 {
9 "name": "pauli_x",
10 "sites": [i],
11 "strength": {"distribution": "truncated_normal", "mean": 0.05, "std": 0.02},
12 }
13 for i in range(num_qubits)
14])
15
16circuit_params = StrongSimParams(
17 observables=[Observable("z", site) for site in range(num_qubits)],
18 num_traj=32,
19 max_bond_dim=8,
20 random_seed=11,
21)
22
23circuit_result = sim.run(State(num_qubits, initial="zeros"), circuit, circuit_params, circuit_noise)
24sampled = [proc["strength"] for proc in circuit_result.noise_model.processes]
25print(f"truncated-normal bit-flip rates: {[f'{s:.4f}' for s in sampled]}")
26print(f"final <Z_0>: {circuit_result.expectation_values[0][0]:.4f}")
truncated-normal bit-flip rates: ['0.0278', '0.0501', '0.0553']
final <Z_0>: 0.6644+0.0000j
5. Long-range crosstalk¶
Non-adjacent pairs use the longrange_crosstalk_{ab} naming convention; YAQS attaches per-site Pauli factors automatically:
1lr_model = NoiseModel([
2 {"name": "longrange_crosstalk_xy", "sites": [0, 2], "strength": 0.05},
3])
4sampled = lr_model.sample(rng=0)
5print("sites:", sampled.processes[0]["sites"])
6print("strength:", sampled.processes[0]["strength"])
7print("factors:", [f.shape for f in sampled.processes[0]["factors"]])
sites: [0, 2]
strength: 0.05
factors: [(2, 2), (2, 2)]