Hamiltonian Simulation
Overview
The unitarylab_algorithms.hamiltonian_simulation package provides five methods for approximating the time-evolution operator of a Hamiltonian . Except for the Cartan decomposition, the other four methods all apply a unified input formatting check (_format_system) and accept the same core inputs (H, t, error), while differing in approximation strategy, circuit depth, and accuracy guarantees.
| Method | Class | Strategy |
|---|---|---|
| Suzuki-Trotter Product Formula Evolution Algorithm | TrotterAlgorithm | Product formula decomposition |
| qDrift Algorithm | QDriftAlgorithm | Randomized product formula |
| Taylor-Series Hamiltonian Simulation | TaylorAlgorithm | Truncated Taylor series |
| Quantum Signal Processing Hamiltonian Simulation (QSP-HS) | QSPHSAlgorithm | Quantum signal processing polynomial |
| Cartan Decomposition Algorithm | CartanDecompositionAlgorithm | Lie algebra Cartan–Lax flow |
Naming Ambiguity Warning: The
QSPHSAlgorithmin this package is located atunitarylab_algorithms.hamiltonian_simulation.qsp.algorithmand is used for approximating Hamiltonian time evolution; theunitarylab_algorithms.linear_algebrapackage also has a folder with the same name,qsp/, whoseQSPAlgorithmis used for solving systems of linear equations (see Linear Algebra Algorithms). The two are completely different in functionality and have different import paths — please be careful to distinguish between them when using them.
Choosing a Method
| Scenario | Recommended Method |
|---|---|
| Short evolution, simple Hamiltonian | Suzuki-Trotter (1st or 2nd order) |
| Stochastic / randomized simulation | qDrift |
| High precision, moderate depth | Taylor series |
| Sparse Hamiltonian, long evolution | QSP-HS |
| Real symmetric Hamiltonian, exact decomposition | Cartan decomposition |
Unified Note: The status and error Parameters
- For all 5 algorithms in this package,
.run()unconditionally setsself.statusto"success"upon successful completion of the computation, and the success argument passed to_build_return_dict()is alwaysTrue— in other words, the top-levelstatusin the returned dictionary is always'ok', regardless of whether the approximation error actually reaches the expected level oferror. If you need to assess accuracy, you should compare theFrobenius norm of errorin the returned result (orFinal total errorfor Cartan) against theerrorthreshold you passed in yourself. errorparticipates in precision or scale control in QSP-HS, Taylor, and Trotter; qDrift controls precision mainly throughsteps.
Suzuki-Trotter Product Formula Evolution Algorithm
Background
The Trotter–Suzuki product formula approximates as (first order or higher). The method is simple to implement and is the most widely used Hamiltonian simulation method in practice.
First-order error: Second order (Suzuki):
Import
from unitarylab_algorithms import TrotterAlgorithm.run() Parameters
def run(self, H: np.ndarray, t: float, error: float, order: int = 1, steps: int = 1000,
backend='torch', device='cpu', dtype=np.complex128)| Parameter | Type | Default | Description |
|---|---|---|---|
H | np.ndarray | — (required) | Hermitian Hamiltonian matrix (square); a non-Hermitian or non-square matrix raises a ValueError; if the dimension is not a power of 2, it is automatically zero-padded to the nearest power of 2 |
t | float | — (required) | Total evolution time |
error | float | — (required) | Target approximation error; actually used to compute the upper bound on steps — it is not an unused placeholder parameter |
order | int | 1 | Order of the Trotter–Suzuki formula (1 or a higher even order) |
steps | int | 1000 | Upper bound on the number of time steps — the number of steps actually used is the smaller of this upper bound and the theoretical formula , where is the spectral norm of and is the number of qubits |
Return Value
circuit_path is a list of two paths: [the full repeated-circuit path, the single time-slice circuit path], unlike the other 4 algorithms in this package (which use a single string).
{
'status': 'ok', # always 'ok'; does not reflect the actual approximation accuracy
'circuit_path': ['/path/.../trotter_full.svg', '/path/.../trotter_slice.svg'],
'plot': [{'format': 'txt', 'filename': 'trotter_hamiltonian_simulation_algorithm_result.txt'}],
'circuit': <Circuit>, # the full repeated circuit
'Approximate evolution matrix': array([...]),
'Exact evolution matrix': array([...]),
'Frobenius norm of error': 1.2e-05,
}Example
import numpy as np
from unitarylab_algorithms import TrotterAlgorithm
H = np.array([[2, 1], [1, 3]])
algo = TrotterAlgorithm()
result = algo.run(H=H, t=1.0, error=1e-8, order=1, steps=1000)
print(result['Frobenius norm of error'])Quick Demo
from unitarylab_algorithms.hamiltonian_simulation.trotter.algorithm import test
test()Notes
stepsis actually an upper bound, not a fixed step count — the real step count is automatically computed by a formula based ont,order,error, and the spectral norm of , and the smaller of that value and the passed-instepsis used; therefore passing a very largestepsdoes not necessarily yield that many steps.errorparticipates in computing the upper bound on the step count.- When
orderis a higher order (e.g., 2, 4), the Suzuki recursive formula (_recurse) is used; in theory only order 1 or even orders are supported.
qDrift Algorithm
Background
qDrift is a randomized product formula that samples Pauli terms according to the magnitude of their coefficients and applies them with appropriately rescaled angles. This produces an unbiased stochastic approximation that converges to the exact evolution as the number of samples increases.
Gate complexity: , where
Import
from unitarylab_algorithms import QDriftAlgorithm.run() Parameters
def run(self, H: np.ndarray, t: float, error: float, steps: int = 5000,
backend='torch', device='cpu', dtype=np.complex128)| Parameter | Type | Default | Description |
|---|---|---|---|
H | np.ndarray | — (required) | Hermitian Hamiltonian matrix |
t | float | — (required) | Total evolution time |
error | float | — (required) | Target approximation error — the only truly unused error among the 5 algorithms in this package; it is only used for the validity check error > 0 |
steps | int | 5000 | Number of random samples (larger values give higher accuracy; circuit depth grows linearly) |
Return Value
{
'status': 'ok',
'circuit_path': '/path/to/qdrift_algorithm_circuit.svg',
'plot': [{'format': 'txt', 'filename': 'qdrift_algorithm_result.txt'}],
'circuit': <Circuit>,
'Approximate evolution matrix': array([...]),
'Exact evolution matrix': array([...]),
'Frobenius norm of error': 3.4e-03,
}Example
import numpy as np
from unitarylab_algorithms import QDriftAlgorithm
H = np.array([[2, 1], [1, 3]])
algo = QDriftAlgorithm()
result = algo.run(H=H, t=1.0, error=1e-8, steps=5000)
print(result['Frobenius norm of error'])Quick Demo
from unitarylab_algorithms.hamiltonian_simulation.qdrift.algorithm import test
test()Notes
erroris used only for validity checking;stepsis the key parameter that controls accuracy.- Because this relies on random sampling (
np.random.choice, with no fixed seed set), the specific circuit and theFrobenius norm of errorwill differ on every run and are not reproducible; to reproduce results, you need to setnp.random.seed(...)yourself before calling it.
Taylor-Series Hamiltonian Simulation
Background
The Taylor method expands as a truncated Taylor series up to degree , and then implements each term using a linear combination of unitaries (LCU). It can achieve precision with terms.
Import
from unitarylab_algorithms import TaylorAlgorithm.run() Parameters
def run(self, H: np.ndarray, t: float, error: float, degree: int = 15,
backend='torch', device='cpu', dtype=np.complex128)| Parameter | Type | Default | Description |
|---|---|---|---|
H | np.ndarray | — (required) | Hermitian Hamiltonian matrix |
t | float | — (required) | Total evolution time |
error | float | — (required) | Target approximation error; actually used in computing the effective degree — see the Notes below |
degree | int | 15 | Upper bound on the degree of the Taylor expansion; the effective degree is hard-capped at 15 — even if a larger value is passed in, it will not exceed 15 |
The default value in the web front end’s
parameters.jsonisd=10, which is inconsistent with.run()’s own default value ofdegree=15; if the Python API is called without explicitly passing this parameter,15is used.
Return Value
{
'status': 'ok',
'circuit_path': '/path/to/taylor_hamiltonian_simulation_algorithm_circuit.svg',
'plot': [{'format': 'txt', 'filename': 'taylor_hamiltonian_simulation_algorithm_result.txt'}],
'circuit': <Circuit>, # LCU circuit object (not decomposed)
'Approximate evolution matrix': array([...]),
'Exact evolution matrix': array([...]),
'Frobenius norm of error': 5.6e-06,
}Example
import numpy as np
from unitarylab_algorithms import TaylorAlgorithm
H = np.array([[2, 1], [1, 3]])
algo = TaylorAlgorithm()
result = algo.run(H=H, t=1.0, error=1e-8, degree=15)
print(result['Frobenius norm of error'])Quick Demo
from unitarylab_algorithms.hamiltonian_simulation.taylor.algorithm import test
test()Notes
- The effective expansion degree is computed as
degree = min(max(degree, ⌈1.5λ + 1.5·ln(1/error)⌉), 15)(where ), with a hard upper bound of 15 — passingdegree=100still results in a computation using no more than 15 degrees. This is an inherent limitation of the current implementation, not a configuration oversight. errorparticipates in the effective-degree calculation above.- The saved circuit diagram corresponds to
circuit.decompose()(the decomposed gate sequence), whereas the returnedcircuitfield is the undecomposed LCU circuit object — the two have different structures.
Quantum Signal Processing Hamiltonian Simulation (QSP-HS)
Background
QSP-based Hamiltonian simulation constructs a quantum circuit that approximates the time-evolution operator by encoding the eigenvalues of as a signal and applying a sequence of controlled unitaries and single-qubit rotations. For sparse Hamiltonians, QSP can achieve near-optimal gate complexity. Internally, is first block-encoded (block_encode(H, method="nagy")), then the two components and are constructed via Chebyshev/Bessel coefficients, and finally combined using LCU.
Gate complexity:
Import
from unitarylab_algorithms import QSPHSAlgorithmThe class name for this module is
QSPHSAlgorithm(distinct from theQSPAlgorithmin the identically named folder underlinear_algebra.qsp— see the notice at the top of this page).
.run() Parameters
def run(self, H: np.ndarray, t: float, error: float, degree: int = 15, beta: float = 0.7,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| Parameter | Type | Default | Description |
|---|---|---|---|
H | np.ndarray | — (required) | Hermitian Hamiltonian matrix |
t | float | — (required) | Total evolution time; internally, the number of time slices time_slices is automatically chosen based on degree (increasing as a power of 2), and only a single representative time-slice QSP circuit is constructed. The evolution matrix for that slice is then combined via classical matrix-power composition using np.linalg.matrix_power(..., time_slices); a separate circuit is not constructed for each slice |
error | float | — (required) | Target approximation error; actually used in estimating the QSP degree required per time slice |
degree | int | 15 | Upper bound on the degree of the QSP polynomial; in the web front end’s parameters.json, this parameter is named d (not degree) — when calling the Python API, use degree |
beta | float | 0.7 | Preconditioning factor for numerical stability; must satisfy , otherwise a ValueError is raised |
Return Value
{
'status': 'ok',
'circuit_path': '/path/to/qsp_hamiltonian_simulation_algorithm_circuit.svg',
'plot': [{'format': 'txt', 'filename': 'qsp_hamiltonian_simulation_algorithm_result.txt'}],
'circuit': <Circuit>, # the QSP+LCU circuit for a single time slice
'Approximate evolution matrix': array([...]),
'Exact evolution matrix': array([...]),
'Frobenius norm of error': 8.9e-07,
}Example
import numpy as np
from unitarylab_algorithms import QSPHSAlgorithm
H = np.array([[2, 1], [1, 3]])
algo = QSPHSAlgorithm()
result = algo.run(H=H, t=1.0, error=1e-8, degree=15, beta=0.7)
print(result['Frobenius norm of error'])Quick Demo
from unitarylab_algorithms.hamiltonian_simulation.qsp.algorithm import test
test()Notes
errorparticipates in estimating both the number of time slices and the degree required per slice (_estimate_required_degree).- In the web front end’s parameter panel, this algorithm’s degree parameter is named
d, which is inconsistent with thedegreeparameter name of the Python.run()method — this is a naming inconsistency; when calling the Python API, usedegree.
Cartan Decomposition Algorithm
Background
The Cartan–Lax flow algorithm decomposes the time-evolution operator by decomposing the Lie algebra into a symmetric subalgebra and an antisymmetric space . The resulting circuit has the form . Internally this delegates directly to unitarylab.library.hamiltonian.hamiltonian_simulation(H, evol_time, method='cartan-lax', ...).
Requirement: must be a real symmetric matrix (however, unlike the other 4 algorithms, this algorithm does not perform explicit Hermitian / square-matrix / power-of-2 dimension validation at the Python layer — that validation happens inside the underlying hamiltonian_simulation() to which it delegates).
Import
from unitarylab_algorithms import CartanDecompositionAlgorithm.run() Parameters
def run(self, H: Union[np.ndarray, list], t: float, error: float,
backend: str = "torch", device: str = "cpu", dtype=np.complex128, **kwargs: Any)| Parameter | Type | Default | Description |
|---|---|---|---|
H | np.ndarray | list | — (required) | Real symmetric Hamiltonian |
t | float | — (required) | Total evolution time |
error | float | — (required) | Stopping tolerance for the norm of the off-diagonal component, passed to the underlying solver as target_error |
evol_time (**kwargs) | float | t | Overrides the evolution time passed to the underlying simulator |
lr (**kwargs) | float | 1e-3 | Base integration step size for the Lax flow |
max_steps (**kwargs) | int | 100000 | Upper bound on the number of Lax update steps |
reps (**kwargs) | int | 5000 | Iteration budget before adaptive scaling |
Return Value
The field names in the returned output are completely different from the other 4 algorithms (the other 4 uniformly use Approximate evolution matrix/Exact evolution matrix/Frobenius norm of error, whereas this algorithm uses):
{
'status': 'ok',
'circuit_path': '/path/to/cartan_algorithm_circuit.svg',
'plot': [{'format': 'txt', 'filename': 'cartan_algorithm_result.txt'}],
'circuit': <Circuit>,
'Evolution result': <the evolution-result object returned by the underlying simulator>,
'Final total error': 4.2e-08,
'Computation time (s)': 0.0345, # the only algorithm that includes computation time in its output
'Exact evolution': array([...]), # the exact evolution matrix exp(-iHt); the key name also differs from the other algorithms (Exact evolution matrix)
}Example
import numpy as np
from unitarylab_algorithms import CartanDecompositionAlgorithm
H = np.array([[2, 1], [1, 2]])
algo = CartanDecompositionAlgorithm()
result = algo.run(H=H, t=1.0, error=1e-3)
print(result['Final total error'])Quick Demo
from unitarylab_algorithms.hamiltonian_simulation.cartan.algorithm import test
test()Notes
- The Cartan method currently supports only matrix-form Hamiltonians; Pauli-string input is not yet supported.
- For ill-conditioned Hamiltonians, reducing
lrand increasingrepscan improve convergence. - The output field naming (
Evolution result/Final total error/Exact evolution) is inconsistent with the other 4 algorithms (Approximate evolution matrix/Exact evolution matrix/Frobenius norm of error). When mixing multiple methods, be careful to distinguish between the key names — do not assume that allhamiltonian_simulationalgorithms share a unified output field structure.