Quickstart

This page runs minimal workflows end-to-end: analog and digital simulation, equivalence checking, environmental memory (characterize from the response matrix, then train a surrogate to predict probe density matrices under a control sequence), and Markovian noise digital-twin fitting. Install the package first (installation), then copy the cells below.

Every example in this guide uses show_progress=False on Simulator, MemoryCharacterizer, and NoiseCharacterizer so tqdm progress bars do not clutter the documentation; figures below each cell show the main results.

1. Analog simulation

Néel-initialized transverse-field Ising chain with on-site damping. Staggered \(\langle Z_i \rangle\) spreads and decays in a site-dependent way under open-system evolution:

 1import matplotlib.pyplot as plt
 2import numpy as np
 3
 4from mqt.yaqs import AnalogSimParams, Hamiltonian, NoiseModel, Observable, Simulator, State
 5
 6L = 5
 7state = State(L, initial="Neel")
 8hamiltonian = Hamiltonian.ising(L, J=1.0, g=0.8)
 9noise_model = NoiseModel([
10    {"name": "lowering", "sites": [i], "strength": 0.06} for i in range(L)
11])
12
13params = AnalogSimParams(
14    observables=[Observable("z", site) for site in range(L)],
15    elapsed_time=4.0,
16    dt=0.1,
17    num_traj=16,
18    max_bond_dim=16,
19    order=2,
20    sample_timesteps=True,
21)
22
23sim = Simulator(show_progress=False)
24result = sim.run(state, hamiltonian, params, noise_model)
25
26heatmap = np.vstack([np.real(v) for v in result.expectation_values])
27fig, ax = plt.subplots(figsize=(6, 3.5), layout="constrained")
28im = ax.imshow(heatmap, aspect="auto", extent=(0, 4.0, L, 0), vmin=-1, vmax=1, cmap="RdBu_r")
29ax.set_xlabel("time")
30ax.set_yticks([x - 0.5 for x in range(1, L + 1)], [str(x) for x in range(L)])
31ax.set_ylabel("site")
32fig.colorbar(im, ax=ax, shrink=0.9, label=r"$\langle Z \rangle$")
33ax.set_title("Staggered magnetization under damping")
Text(0.5, 1.0, 'Staggered magnetization under damping')
../_images/d73c9c92e603bdd2dea3ea611c96cc88a62fb49178d33f9da11773a6d1b043d4.svg

2. Strong simulation

Evolve a short Trotterized Ising circuit and compare final \(\langle Z_i\rangle\) without noise and with an optional NoiseModel. See Strong Simulation for noise sweeps, mid-circuit sampling, and gate modes.

 1from mqt.yaqs import NoiseModel, Observable, StrongSimParams
 2from mqt.yaqs.core.libraries.circuit_library import create_ising_circuit
 3
 4num_qubits = 3
 5qc = create_ising_circuit(L=num_qubits, J=1.0, g=0.8, dt=0.1, timesteps=6)
 6circuit_state = State(num_qubits, initial="zeros")
 7circuit_params = StrongSimParams(
 8    observables=[Observable("z", site) for site in range(num_qubits)],
 9    preset="fast",
10    num_traj=32,
11)
12noise_model = NoiseModel([
13    {"name": "lowering", "sites": [site], "strength": 0.05} for site in range(num_qubits)
14])
15
16clean_result = sim.run(circuit_state, qc, circuit_params)
17noisy_result = sim.run(State(num_qubits, initial="zeros"), qc, circuit_params, noise_model)
18clean_z = np.array([float(np.real(v[0])) for v in clean_result.expectation_values])
19noisy_z = np.array([float(np.real(v[0])) for v in noisy_result.expectation_values])
20
21fig, ax = plt.subplots(figsize=(5, 3), layout="constrained")
22x = np.arange(num_qubits)
23bar_width = 0.35
24ax.bar(x - bar_width / 2, clean_z, bar_width, label="unitary", color="0.55")
25ax.bar(x + bar_width / 2, noisy_z, bar_width, label="with damping", color="C0")
26ax.set_xticks(x, [rf"$\langle Z_{i}\rangle$" for i in range(num_qubits)])
27ax.set_ylim(-1.05, 1.05)
28ax.set_ylabel("expectation value")
29ax.set_title("Digital Ising circuit: optional open-system noise")
30ax.legend(frameon=False)
<matplotlib.legend.Legend at 0x7dbc98575fd0>
../_images/393b5cf0721e988e2be3ebaaa05026b04b118b79a2cbc6e724a5f91958f84d50.svg

3. Equivalence checking

Verify that a native GHZ circuit matches its transpiled decomposition (different gate basis, same unitary) with EquivalenceChecker:

 1from qiskit import transpile
 2from qiskit.circuit import QuantumCircuit
 3
 4from mqt.yaqs import EquivalenceChecker
 5
 6ghz_native = QuantumCircuit(3)
 7ghz_native.h(0)
 8ghz_native.cx(0, 1)
 9ghz_native.cx(1, 2)
10
11ghz_transpiled = transpile(
12    ghz_native,
13    basis_gates=["rz", "sx", "x", "cx"],
14    optimization_level=1,
15)
16
17checker = EquivalenceChecker(representation="mpo", threshold=1e-6)
18equiv = checker.check(ghz_native, ghz_transpiled)
19print(f"equivalent: {equiv['equivalent']}")
20print(f"fidelity: {equiv['fidelity']:.4e}")
21print(f"center-cut operator entropy: {equiv['center_cut_entanglement_entropy']:.4f}")
22print(f"global operator entropy: {equiv['global_entanglement_entropy']:.4f}")
23
24fig, ax = plt.subplots(figsize=(4.5, 3))
25ax.semilogy(equiv["schmidt_values"], "o-")
26ax.set_xlabel("Schmidt index")
27ax.set_ylabel("singular value")
28ax.set_title("Composed operator $W = U_2^\\dagger U_1$")
29fig.tight_layout()
equivalent: True
fidelity: 1.0000e+00
center-cut operator entropy: 0.0000
global operator entropy: 0.0000
../_images/d4e382614b58ab15c1441defc8e1154b3f39449040c4385ce9c40c656c00f0ed.svg

For larger circuits, compiler passes, and OpenQASM inputs, see Equivalence Checking.

4. Characterize environmental memory

Probe a probe qubit coupled to a short chain at an interior temporal cut. The memory spectrum and response matrix show how many independent past branches remain visible at the cut:

 1import numpy as np
 2
 3from mqt.yaqs import AnalogSimParams, Hamiltonian, MemoryCharacterizer
 4from mqt.yaqs.characterization.memory.shared.utils import make_zero_psi
 5
 6length = 4
 7ham = Hamiltonian.ising(length=length, J=1.0, g=1.0)
 8params = AnalogSimParams(dt=0.1, max_bond_dim=16, order=1)
 9mc = MemoryCharacterizer(show_progress=False)
10
11cut, num_interventions = 4, 6
12result = mc.characterize(
13    ham,
14    params,
15    num_interventions=num_interventions,
16    cut=cut,
17    n_pasts=6,
18    n_futures=6,
19    initial_psi=make_zero_psi(length),
20    rng=np.random.default_rng(0),
21)
22sv = result.singular_values(cut)
23v = result.response_matrix(cut)
24
25fig, axes = plt.subplots(1, 2, figsize=(8, 3))
26axes[0].semilogy(sv, "o-")
27axes[0].set_xlabel("mode index")
28axes[0].set_ylabel("singular value")
29axes[0].set_title(rf"Memory spectrum: $S_V(c={cut})={result.entropy(cut):.2f}$")
30
31im = axes[1].imshow(np.abs(v), aspect="auto", cmap="viridis")
32axes[1].set_title(rf"$|\widetilde{{V}}(c)|$, $R(c)={result.modes(cut):.1f}$")
33axes[1].set_xlabel("future probe")
34axes[1].set_ylabel("past probe")
35fig.colorbar(im, ax=axes[1], fraction=0.046, pad=0.04)
36fig.tight_layout()
../_images/da772152755c28e044607dc1d4755e174ac4d3e7b80b4424a2e3bd1d2798ce9c.svg

5. Fit a Markovian noise digital twin (analytical optimization)

Learn Lindblad jump rates from observable trajectories with NoiseCharacterizer using analytical optimization (simulator forward model + CMA-ES trajectory matching).

 1import warnings
 2
 3import numpy as np
 4
 5warnings.filterwarnings("ignore", message=".*special injected samples.*")
 6
 7from mqt.yaqs import AnalogSimParams, Hamiltonian, NoiseCharacterizer, NoiseModel, Observable, State
 8
 9n_sites = 3
10sites = list(range(n_sites))
11hamiltonian = Hamiltonian.ising(n_sites, J=1.0, g=2.0)
12init_state = State(n_sites, initial="zeros")
13fitting_observables = [Observable("y", 0), Observable("z", 0), Observable("y", 1)]
14sim_params = AnalogSimParams(
15    observables=fitting_observables,
16    elapsed_time=0.8,
17    dt=0.1,
18    order=1,
19    sample_timesteps=True,
20)
21reference_model = NoiseModel(
22    [{"name": "pauli_x", "sites": [s], "strength": 0.08} for s in sites]
23    + [{"name": "pauli_y", "sites": [s], "strength": 0.08} for s in sites]
24    + [{"name": "pauli_z", "sites": [s], "strength": 0.08} for s in sites]
25)
26init_guess = NoiseModel(
27    [{"name": "pauli_x", "sites": [s], "strength": 0.35} for s in sites]
28    + [{"name": "pauli_y", "sites": [s], "strength": 0.35} for s in sites]
29    + [{"name": "pauli_z", "sites": [s], "strength": 0.35} for s in sites]
30)
31
32result = NoiseCharacterizer(show_progress=False).characterize(
33    hamiltonian,
34    sim_params,
35    init_state=init_state,
36    init_guess=init_guess,
37    observables=fitting_observables,
38    reference_model=reference_model,
39    x_low=np.zeros(len(init_guess.processes)),
40    x_up=np.full(len(init_guess.processes), 0.5),
41    sigma0=0.05,
42    popsize=8,
43    max_iter=20,
44    seed=42,
45)
46
47times = result.times
48obs_labels = [r"$\langle Y_0\rangle$", r"$\langle Z_0\rangle$", r"$\langle Y_1\rangle$"]
49fig, axes = plt.subplots(1, 2, figsize=(8, 2.8), layout="constrained", sharey=True)
50for ax, traj, title in zip(axes, [result.fit_traj, result.ref_traj], ["learned twin", "reference"], strict=True):
51    im = ax.imshow(
52        traj,
53        aspect="auto",
54        extent=(times[0], times[-1], len(obs_labels), 0),
55        vmin=-1,
56        vmax=1,
57        cmap="RdBu_r",
58    )
59    ax.set_yticks([i + 0.5 for i in range(len(obs_labels))], obs_labels)
60    ax.set_xlabel("time")
61    ax.set_title(title)
62fig.colorbar(im, ax=axes, shrink=0.9, label="expectation")
63fig.suptitle(rf"Twin fit: RMSE={result.trajectory_rmse():.2e}", y=1.02)
Text(0.5, 1.02, 'Twin fit: RMSE=1.58e-02')
../_images/335a666af16702f8cf0d96129c2bdb9e96f33720ea9ab8025cb4e6ec779911f9.svg

See Analytical Optimization Digital Twin from Experimental Trajectories for the full analytical-optimization workflow, experimental-data fitting, held-out prediction, and MCWF fitting.

6. Train a surrogate and predict under controls

Train a causal surrogate with MemoryCharacterizer, then predict the probe-qubit state after one or more control legs. Pass an explicit per-leg list to compare different sequences on the same trained model. Surrogate training requires PyTorch (pip install mqt.yaqs[torch]).

 1rho0 = np.eye(2, dtype=np.complex128) / 2.0
 2ham_sure = Hamiltonian.ising(length=2, J=1.0, g=1.0)
 3
 4model = mc.train(
 5    ham_sure,
 6    params,
 7    num_interventions=1,
 8    n=32,
 9    train_kwargs={"epochs": 30, "batch_size": 8},
10    model_kwargs={"d_model": 32, "nhead": 4, "num_layers": 1, "dim_ff": 64},
11)
12
13hadamard = np.array([[1, 1], [1, -1]], dtype=np.complex128) / np.sqrt(2)
14pauli_x = np.array([[0, 1], [1, 0]], dtype=np.complex128)
15control_sequences = {
16    r"$\mathrm{H}$": [{"unitary": hadamard}],
17    r"$\mathrm{X}$": [{"unitary": pauli_x}],
18}
19
20pauli_ops = {
21    "X": np.array([[0, 1], [1, 0]], dtype=np.complex128),
22    "Y": np.array([[0, -1j], [1j, 0]], dtype=np.complex128),
23    "Z": np.array([[1, 0], [0, -1]], dtype=np.complex128),
24}
25expectations = {
26    label: [
27        float(np.trace(op @ mc.predict(model, rho0, controls, num_interventions=1)).real)
28        for op in pauli_ops.values()
29    ]
30    for label, controls in control_sequences.items()
31}
32
33pauli_names = list(pauli_ops)
34x = np.arange(len(pauli_names))
35width = 0.35
36
37fig, ax = plt.subplots(figsize=(5, 3.5))
38for offset, (label, values) in zip((-width / 2, width / 2), expectations.items()):
39    ax.bar(x + offset, values, width, label=f"control {label}")
40ax.set_xticks(x, pauli_names)
41ax.set_ylabel("expectation value")
42ax.set_title("Probe Pauli expectations for two control sequences")
43ax.legend(frameon=False)
44fig.tight_layout()
../_images/1635db07a3a9140424a761a1c105c4e71da2179466262a82ed3fba23df397c6f.svg

predict also accepts a style string (for example "haar") or a per-leg list mixing unitaries and measure–prepare slots. See Probing Environmental Memory for environmental memory probing and Memory Surrogate Training and Prediction for held-out accuracy checks and exact-reference validation.

7. Where to go next

Goal

Start here

Environmental memory probing

Probing Environmental Memory

Markovian noise digital-twin fitting

Analytical Optimization Digital Twin from Experimental Trajectories

Surrogate training, prediction, and exact validation

Memory Surrogate Training and Prediction

Open-system dynamics, noise, time grids

Noisy Analog Simulation

Bell-curve (log-normal) noise strengths

Realistic Noise Models

Strong simulation, mid-circuit sampling, OpenQASM

Strong Simulation

Accuracy presets and truncation knobs

Configuring Simulation Parameters

Check two circuits for equivalence

Equivalence Checking