Noisy Analog Simulation¶
This guide walks through an open-system analog simulation with the tensor jump method (TJM): build a Hamiltonian, attach a noise model, configure AnalogSimParams, and visualize time-resolved observables.
For Gaussian (bell-curve) noise strengths and static disorder, see Realistic Noise Models. For execution options (parallelism, progress bars), see Configuring the Simulator.
1. Hamiltonian¶
Three equivalent ways to define the transverse-field Ising model \(H = -J \sum_i Z_i Z_{i+1} - g \sum_i X_i\) on an open chain:
1from mqt.yaqs import Hamiltonian, MPO
2
3L = 3
4J = 1
5g = 0.5
6
7# Method 1: built-in constructor
8H_0 = Hamiltonian.ising(L, J, g)
9
10# Method 2: generic Pauli interface
11H_0 = Hamiltonian.pauli(
12 length=L,
13 two_body=[(-J, "Z", "Z")],
14 one_body=[(-g, "X")],
15 bc="open",
16)
17
18# Method 3: explicit Pauli-string MPO
19terms = [(-J, f"Z{i} Z{i+1}") for i in range(L - 1)] + [(-g, f"X{i}") for i in range(L)]
20mpo = MPO()
21mpo.from_pauli_sum(terms=terms, length=L)
22H_0 = Hamiltonian.from_mpo(mpo)
2. Initial state and noise model¶
1from mqt.yaqs import NoiseModel, State
2
3state = State(L, initial="zeros")
4# Alternative: State(L, initial="haar-random", pad=4)
5
6gamma = 0.1
7noise_model = NoiseModel([
8 {"name": name, "sites": [i], "strength": gamma}
9 for i in range(L)
10 for name in ["lowering", "pauli_z"]
11])
Pass a float for each strength here. For distribution-valued strengths (Gaussian and other distributions), see Realistic Noise Models.
3. Simulation parameters¶
1from mqt.yaqs import AnalogSimParams, Observable
2
3sim_params = AnalogSimParams(
4 observables=[Observable("x", site) for site in range(L)],
5 elapsed_time=10,
6 dt=0.1,
7 num_traj=100,
8 max_bond_dim=4,
9 svd_threshold=1e-6,
10 order=2,
11 sample_timesteps=True,
12)
Optional tdvp_sweeps (default 1) runs multiple symmetric TDVP substeps per physical step dt, improving unitary accuracy without changing the noise timestep.
Evolution integrator: analog simulations default to EvolutionMode.TDVP (two-site TDVP sweeps). EvolutionMode.BUG is available as an alternative on AnalogSimParams when you want the BUG integrator instead.
4. Reproducible stochastic runs¶
With num_traj > 1, each run() call averages independent quantum-jump trajectories. Set random_seed to fix the pseudorandom stream across trajectories (and for distribution-valued noise strengths):
1import copy
2
3import numpy as np
4
5from mqt.yaqs import AnalogSimParams, Observable, Simulator
6
7repro_params = AnalogSimParams(
8 observables=[Observable("x", site) for site in range(L)],
9 elapsed_time=1.0,
10 dt=0.1,
11 num_traj=50,
12 max_bond_dim=4,
13 svd_threshold=1e-6,
14 order=2,
15 sample_timesteps=True,
16 random_seed=42,
17)
18
19sim = Simulator(parallel=True, show_progress=False)
20
21
22def run_reproducible() -> list[np.ndarray]:
23 st = copy.deepcopy(state)
24 params = copy.deepcopy(repro_params)
25 result = sim.run(st, H_0, params, copy.deepcopy(noise_model))
26 return result.expectation_values
27
28
29first_run = run_reproducible()
30second_run = run_reproducible()
31assert all(np.allclose(a, b) for a, b in zip(first_run, second_run, strict=True))
32print("Both runs produced identical trajectory-averaged observables.")
Both runs produced identical trajectory-averaged observables.
The same random_seed field exists on StrongSimParams and WeakSimParams.
5. Run and visualize¶
1result = sim.run(state, H_0, sim_params, noise_model)
1import matplotlib.pyplot as plt
2
3heatmap = result.expectation_values
4
5fig, ax = plt.subplots(figsize=(5, 3))
6im = ax.imshow(heatmap, aspect="auto", extent=(0, 10, L, 0), vmin=0, vmax=0.5)
7ax.set_xlabel("Time")
8ax.set_yticks([x - 0.5 for x in range(1, L + 1)], [str(x) for x in range(L)])
9ax.set_ylabel("Site")
10
11fig.subplots_adjust(top=0.95, right=0.88)
12cbar_ax = fig.add_axes(rect=(0.9, 0.11, 0.025, 0.8))
13cbar = fig.colorbar(im, cax=cbar_ax)
14cbar.ax.set_title(r"$\langle X \rangle$")
15plt.tight_layout()
16plt.show()