Configuring Simulation Parameters¶
YAQS separates what you evolve (State, circuits, Hamiltonians) from how you truncate and sample via parameter objects passed to run():
Class |
Use when |
|---|---|
Open-system or unitary time evolution (TDVP / BUG, MCWF trajectories, Lindblad-style paths). |
|
Noisy strong digital simulation (per-trajectory MPS evolution with observables). |
|
Noisy weak digital simulation (shot-based sampling; you set |
This page shows how to construct each class. For Simulator execution options (parallelism, progress bars), see Configuring the Simulator.
Observable string names¶
Observable accepts a string gate name as its first argument. YAQS resolves the name to the corresponding operator internally — you do not import gate classes for standard measurements.
String |
Meaning |
Example |
|---|---|---|
|
Single-qubit Pauli operators |
|
|
Other single-qubit gates from the built-in library |
|
|
Two-qubit Pauli strings |
|
|
Bipartite entanglement entropy across a cut |
|
|
Schmidt spectrum across a cut |
|
bitstring / |
Projection-valued measurement onto a computational basis state |
For custom unitaries and circuit gates, use Custom and Qiskit Gates in YAQS — those workflows still use GateLibrary or Qiskit circuits directly.
Start with a preset¶
You do not need to tune every numerical knob before running a simulation. Pick a preset and let it fill in the truncation and sampling settings you may be unfamiliar with (svd_threshold, max_bond_dim, num_traj on analog/strong runs, and krylov_tol).
All three *SimParams classes accept a keyword-only preset argument (default "balanced"):
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"fast"— qualitative exploration and quick tests; not intended for strict dense comparisons."balanced"— recommended default for exploratory work."accurate"— high-quality production settings."exact"— strict reference/debug preset with minimal internal numerical relaxation. Stochastic trajectory sampling, finite time steps, and model error still apply; this is not mathematically exact.
svd_threshold controls tensor-network SVD truncation (bond truncation). krylov_tol controls the adaptive Krylov/Lanczos matrix exponential inside TDVP updates. These are independent: tightening one does not change the other. trunc_mode (default "discarded_weight") is unchanged across presets. The chosen preset name is stored on the object as params.preset.
Override only what you need¶
Explicit constructor arguments override the preset; everything you omit keeps the preset value.
That is the intended workflow when you know some settings but not all:
Choose the closest preset (
"fast","balanced","accurate", or"exact").Pass only the fields you want to change.
Leave the rest unset — they stay at the preset defaults.
Overridable preset fields:
Argument |
What it controls |
|---|---|
|
SVD bond truncation during MPS/MPO updates |
|
Hard cap on bond dimension ( |
|
Trajectory count (analog / strong only) |
|
Adaptive Krylov/Lanczos matrix exponential in TDVP |
WeakSimParams always requires shots separately; shots is not part of any preset.
If you omit an overridable argument, the preset supplies it. If you pass a value explicitly, that value wins for that field only — the other preset fields are unchanged. For max_bond_dim, omit the argument to keep the preset cap; pass None explicitly to remove the cap.
Recommended usage¶
1from mqt.yaqs import (
2 SIMULATION_PRESETS,
3 AnalogSimParams,
4 Observable,
5 StrongSimParams,
6 WeakSimParams,
7)
8
9
10def _trunc_summary(params: AnalogSimParams | StrongSimParams | WeakSimParams) -> dict[str, object]:
11 """Collect preset-related fields for display."""
12 out: dict[str, object] = {
13 "preset": params.preset,
14 "svd_threshold": params.svd_threshold,
15 "max_bond_dim": params.max_bond_dim,
16 "krylov_tol": params.krylov_tol,
17 }
18 if isinstance(params, WeakSimParams):
19 out["shots"] = params.shots
20 else:
21 out["num_traj"] = params.num_traj
22 return out
Pick a preset — no other truncation arguments required:
1# Default: balanced preset fills in all truncation settings
2analog_params = AnalogSimParams()
3print("default", _trunc_summary(analog_params))
4
5for name in ("fast", "balanced", "accurate", "exact"):
6 params = AnalogSimParams(preset=name)
7 print(name, _trunc_summary(params))
default {'preset': 'balanced', 'svd_threshold': 1e-06, 'max_bond_dim': 128, 'krylov_tol': 0.0001, 'num_traj': 256}
fast {'preset': 'fast', 'svd_threshold': 0.001, 'max_bond_dim': 16, 'krylov_tol': 0.001, 'num_traj': 128}
balanced {'preset': 'balanced', 'svd_threshold': 1e-06, 'max_bond_dim': 128, 'krylov_tol': 0.0001, 'num_traj': 256}
accurate {'preset': 'accurate', 'svd_threshold': 1e-09, 'max_bond_dim': 4096, 'krylov_tol': 1e-06, 'num_traj': 1024}
exact {'preset': 'exact', 'svd_threshold': 1e-13, 'max_bond_dim': None, 'krylov_tol': 1e-12, 'num_traj': 1024}
Override one field; the rest stay from "balanced":
1balanced = AnalogSimParams(preset="balanced")
2tighter_krylov = AnalogSimParams(preset="balanced", krylov_tol=1e-8)
3
4print("balanced preset only", _trunc_summary(balanced))
5print("override krylov_tol only", _trunc_summary(tighter_krylov))
6# svd_threshold, max_bond_dim, and num_traj still come from "balanced"
7assert tighter_krylov.svd_threshold == balanced.svd_threshold
8assert tighter_krylov.max_bond_dim == balanced.max_bond_dim
9assert tighter_krylov.num_traj == balanced.num_traj
balanced preset only {'preset': 'balanced', 'svd_threshold': 1e-06, 'max_bond_dim': 128, 'krylov_tol': 0.0001, 'num_traj': 256}
override krylov_tol only {'preset': 'balanced', 'svd_threshold': 1e-06, 'max_bond_dim': 128, 'krylov_tol': 1e-08, 'num_traj': 256}
Override several fields when you know exactly what you want; the remaining preset fields still apply:
1custom_params = AnalogSimParams(
2 preset="fast", # start from fast defaults for everything else
3 max_bond_dim=512,
4 num_traj=32,
5)
6_trunc_summary(custom_params)
{'preset': 'fast',
'svd_threshold': 0.001,
'max_bond_dim': 512,
'krylov_tol': 0.001,
'num_traj': 32}
Weak simulation: set shots yourself, use a preset for truncation:
1weak_params = WeakSimParams(
2 shots=1024,
3 preset="fast",
4)
5_trunc_summary(weak_params)
{'preset': 'fast',
'svd_threshold': 0.001,
'max_bond_dim': 16,
'krylov_tol': 0.001,
'shots': 1024}
AnalogSimParams¶
Besides the preset (and any overrides), you typically set the time grid (elapsed_time, dt), observables, and whether to record intermediate times (sample_timesteps).
1L = 4
2observables = [Observable("z", site) for site in range(L)]
3
4analog = AnalogSimParams(
5 observables=observables,
6 elapsed_time=0.2,
7 dt=0.05,
8 preset="accurate",
9)
10_trunc_summary(analog)
{'preset': 'accurate',
'svd_threshold': 1e-09,
'max_bond_dim': 4096,
'krylov_tol': 1e-06,
'num_traj': 1024}
Need a smaller bond cap for a quick test, but keep the rest of "accurate"?
1analog_quick = AnalogSimParams(
2 observables=observables,
3 elapsed_time=0.2,
4 dt=0.05,
5 preset="accurate",
6 max_bond_dim=256,
7)
8_trunc_summary(analog_quick)
{'preset': 'accurate',
'svd_threshold': 1e-09,
'max_bond_dim': 256,
'krylov_tol': 1e-06,
'num_traj': 1024}
Pass the resulting object to run() together with a State and Hamiltonian (see Noisy Analog Simulation).
StrongSimParams¶
Used for noisy strong circuit simulation. Provide observables and optionally enable layer sampling (see Noisy Circuit Simulation).
Two-qubit gate mode (gate_mode)¶
Digital circuit simulation on an MPS defaults to gate_mode="mpo" (generic MPO–MPS application): nearest-neighbor gates use the same local TEBD/SVD path as swaps, and long-range gates contract an extended gate MPO site-wise (library leg ordering, MPS virtual index before MPO virtual index) followed by compression with svd_threshold and max_bond_dim. Other modes differ only in how two-qubit gates are applied:
swaps— TEBD/SVD for every two-qubit gate; long-range gates are routed with adjacent SWAP insertion before and after the local update.tdvp— TEBD/SVD on nearest-neighbor gates; long-range gates use the generator MPO + two-site TDVP (2TDVP) on a local window.full-tdvp— TDVP (generator MPO + 2TDVP on a local window) on every two-qubit gate.
Matrix-backed custom gates (from Qiskit UnitaryGate or other unknown 1-/2-qubit unitaries) have no
analytic generator. In gate_mode="tdvp" or "full-tdvp", those gates use TEBD on nearest-neighbor
pairs and the MPO path on long-range pairs instead of the TDVP generator window. See Custom and Qiskit Gates in YAQS
for the full gate translation and custom-gate workflow.
Long-range gates in gate_mode="tdvp" apply 2TDVP on the gate support window via evolve_window.
Use tdvp_sweeps (default 1) to split each TDVP evolution step into multiple substeps of equal total time. Values greater than 1 are opt-in and may improve accuracy on some circuits. The setting applies to all TDVP kernels on AnalogSimParams, StrongSimParams, and WeakSimParams.
Use tdvp_mode to select the TDVP integrator: "1site" (1TDVP), "2site" (2TDVP), or "dynamic" (adaptive single/two-site updates). The default is "2site" (2TDVP) on AnalogSimParams, StrongSimParams, and WeakSimParams. Pass "dynamic" explicitly for adaptive 1/2-site switching during analog evolution.
Substep geometry: each substep is symmetric (left-to-right then right-to-left) at evolution time step_time / tdvp_sweeps for analog (dt) and digital gates. The total generator time applied to one digital gate remains 1 across all substeps. Noise and dissipation after TDVP still use the full physical step dt in analog simulation.
1strong = StrongSimParams(
2 observables=[Observable("z", 0)],
3 gate_mode="tdvp",
4 tdvp_sweeps=2,
5 preset="accurate",
6)
7_trunc_summary(strong)
{'preset': 'accurate',
'svd_threshold': 1e-09,
'max_bond_dim': 4096,
'krylov_tol': 1e-06,
'num_traj': 1024}
1strong_default = StrongSimParams(
2 observables=[Observable("z", 0)],
3 preset="accurate",
4)
5_trunc_summary(strong_default)
{'preset': 'accurate',
'svd_threshold': 1e-09,
'max_bond_dim': 4096,
'krylov_tol': 1e-06,
'num_traj': 1024}
WeakSimParams¶
Used for noisy weak simulation. shots is always required and is not part of the preset.
YAQS stores weak-simulation measurement histograms in Result.counts as a dict[int, int]. The integer key encodes the measured bitstring with site 0 as the least-significant bit (little-endian). This matches Qiskit’s default convention if you interpret Qiskit bitstrings (c_{n-1}...c_0) via int(bitstring, 2).
1weak_balanced = WeakSimParams(shots=1000)
2weak_exact = WeakSimParams(shots=1000, preset="exact")
3print("balanced", _trunc_summary(weak_balanced))
4print("exact", _trunc_summary(weak_exact))
balanced {'preset': 'balanced', 'svd_threshold': 1e-06, 'max_bond_dim': 128, 'krylov_tol': 0.0001, 'shots': 1000}
exact {'preset': 'exact', 'svd_threshold': 1e-13, 'max_bond_dim': None, 'krylov_tol': 1e-12, 'shots': 1000}
See Weak Circuit Simulation for a full example with measurement histograms.
Reference: preset table in code¶
The built-in values are defined in SIMULATION_PRESETS:
1SIMULATION_PRESETS
{'fast': {'svd_threshold': 0.001,
'max_bond_dim': 16,
'num_traj': 128,
'krylov_tol': 0.001},
'balanced': {'svd_threshold': 1e-06,
'max_bond_dim': 128,
'num_traj': 256,
'krylov_tol': 0.0001},
'accurate': {'svd_threshold': 1e-09,
'max_bond_dim': 4096,
'num_traj': 1024,
'krylov_tol': 1e-06},
'exact': {'svd_threshold': 1e-13,
'max_bond_dim': None,
'num_traj': 1024,
'krylov_tol': 1e-12}}