Building Hamiltonians

Analog simulations take a Hamiltonian as the operator argument to run(). Most models are built as matrix product operators (MPOs) under the hood; the Hamiltonian wrapper materialises once at construction and can be reused across parameter sweeps.

This page covers the factory methods in the library. For open-system evolution after the Hamiltonian is defined, see Noisy Analog Simulation.

Hamiltonian versus MPO

Layer

Role

Hamiltonian

User-facing type passed to Simulator.run

MPO

Tensor-network operator; built by factories below

Typical patterns:

  • Preset classmethodsHamiltonian.ising(...), Hamiltonian.pauli(...), etc.

  • Wrap an MPOHamiltonian.from_mpo(mpo) after MPO.bose_hubbard(...) or a custom build.

  • Manual dataHamiltonian(tensors=...) or Hamiltonian(matrix=...) / sparse_matrix=... for small dense/sparse backends (MCWF / Lindblad).

Access the internal MPO with H.mpo when you need bond dimension or tensor cores.

Built-in models (quick reference)

Model

Entry point

Local dimension per site

Transverse-field Ising

ising()

2 (qubits)

Heisenberg

heisenberg()

2

Generic Pauli sums

pauli() or from_pauli_sum()

2

1D Fermi–Hubbard

fermi_hubbard_1d()

4 (fermionic) or 2 (Jordan–Wigner)

Bose–Hubbard

bose_hubbard()Hamiltonian.from_mpo

local_dim (boson occupation cutoff)

Coupled transmon chain

coupled_transmon()

alternating qubit / resonator dims

Trapped ion (position grid)

trapped_ion()Hamiltonian.from_mpo

grid points per ion (1–2 ions)

Open (bc="open") and periodic (bc="periodic") boundaries are supported on the Pauli builders.

Pauli-string Hamiltonians

Ising model (shortcut)

The transverse-field Ising Hamiltonian on an open chain is

\[ H = -J \sum_i Z_i Z_{i+1} - g \sum_i X_i . \]
1from mqt.yaqs import Hamiltonian
2
3L = 4
4J, g = 1.0, 0.5
5H_ising = Hamiltonian.ising(L, J, g)

Structured one- and two-body Pauli terms

pauli() expands nearest-neighbour two_body and on-site one_body lists into Pauli strings automatically:

1H_ising = Hamiltonian.pauli(
2    length=L,
3    two_body=[(-J, "Z", "Z")],
4    one_body=[(-g, "X")],
5    bc="open",
6)

The Heisenberg model is available as a one-liner as well:

1H_heisenberg = Hamiltonian.heisenberg(L, Jx=1.0, Jy=1.0, Jz=1.0, h=0.2)

Explicit Pauli strings (from_pauli_sum)

For arbitrary Pauli strings—including long-range couplings—pass (coefficient, spec) pairs to from_pauli_sum(). Each spec lists operators with site indices, e.g. "Z0 Z3" or "X2":

1from mqt.yaqs import MPO
2
3terms = [(-J, f"Z{i} Z{i+1}") for i in range(L - 1)] + [(-g, f"X{i}") for i in range(L)]
4mpo = MPO()
5mpo.from_pauli_sum(terms=terms, length=L)
6H_custom = Hamiltonian.from_mpo(mpo)

Long-range terms are ordinary entries in terms:

terms.append((0.1, "Z0 Z3"))  # Z on sites 0 and 3

Pauli labels are I, X, Y, Z (case-insensitive). Only physical_dimension=2 is supported for this builder.

Fermi–Hubbard (1D)

fermi_hubbard_1d() implements

\[ H = -t \sum_{i,\sigma} \left(c^\dagger_{i,\sigma} c_{i+1,\sigma} + \mathrm{h.c.}\right) + U \sum_i n_{i,\uparrow} n_{i,\downarrow} \]

(open boundaries, no chemical potential).

Fermionic sites (default)

One physical site has local dimension 4 with basis \(|0\rangle, |\!\downarrow\rangle, |\!\uparrow\rangle, |\!\uparrow\downarrow\rangle\). Ladder operators act on the composite ↑/↓ space per site (not a Jordan–Wigner qubit chain across sites).

1num_sites = 4
2t, u = 1.0, 0.5
3
4H_fermi = Hamiltonian.fermi_hubbard_1d(num_sites, t=t, u=u)

Pair with State using physical_dimensions=[4] * num_sites when building product Fock states (see Initializing Quantum States).

Jordan–Wigner Pauli chain

Pass jordan_wigner=True for a qubit chain in the order 1↑, 1↓, 2↑, 2↓, … Here length is the number of spin orbitals (must be even):

1num_orbitals = 2 * num_sites
2H_jw = Hamiltonian.fermi_hubbard_1d(num_orbitals, t=t, u=u, jordan_wigner=True)

Use this mode when you need Pauli-string semantics with full JW signs between orbitals.

Note

The analog MPO factories omit a chemical potential \(\mu\). For a digital Trotter circuit with \(\mu\), see create_1d_fermi_hubbard_circuit() and Noisy Circuit Simulation.

Correctness of the fermionic and JW MPOs is covered by test_fermi_hubbard_1d_* in the package test suite.

Bose–Hubbard

The Bose–Hubbard model

\[ H = \sum_i \left(\omega\, n_i + \frac{U}{2}\, n_i(n_i-1)\right) - J \sum_i \left(a^\dagger_i a_{i+1} + \mathrm{h.c.}\right) \]

is available on bose_hubbard(). Wrap the MPO for analog simulation:

 1from mqt.yaqs import Hamiltonian, MPO
 2
 3local_dim = 3  # occupations 0, 1, …, local_dim - 1
 4H_bh = Hamiltonian.from_mpo(
 5    MPO.bose_hubbard(
 6        length=3,
 7        local_dim=local_dim,
 8        omega=1.0,
 9        hopping_j=0.2,
10        hubbard_u=0.5,
11    )
12)

Initial states must respect the boson dimension, e.g. State(length, initial="zeros", physical_dimensions=[local_dim] * length).

Coupled transmon–resonator chains

coupled_transmon() builds an alternating chain of transmon qubits and resonators with local dimensions qubit_dim and resonator_dim:

1H_transmon = Hamiltonian.coupled_transmon(
2    length=3,
3    qubit_dim=3,
4    resonator_dim=5,
5    qubit_freq=5.0,
6    resonator_freq=7.0,
7    anharmonicity=-0.3,
8    coupling=0.1,
9)

A full SWAP-style open-system example is in Transmon-Resonator Chain Emulation.

Trapped-ion position grid

trapped_ion() builds a static Hamiltonian for one or two ions on a uniform position grid. Each ion is one MPO site with local dimension equal to the number of grid points. The local terms are a harmonic trap plus a centered finite-difference kinetic energy; for two ions, a softened Coulomb repulsion is compressed into MPO channels (optional SVD truncation via coulomb_cutoff or max_bond_dim).

\[ H = \sum_i \left[-\frac{\hbar^2}{2m_i}\frac{d^2}{dx_i^2} + \tfrac{1}{2} m_i \omega^2 (x_i - q)^2\right] + \frac{g}{\sqrt{(x_1-x_2)^2 + a^2}} \]

(the Coulomb term applies only when two masses are supplied).

 1from mqt.yaqs import Hamiltonian, MPO
 2import numpy as np
 3
 4positions = np.linspace(-6.0, 6.0, 25)
 5H_ion = Hamiltonian.from_mpo(
 6    MPO.trapped_ion(
 7        positions,
 8        masses=[1.0],
 9        omega=1.0,
10        trap_center=0.0,
11    )
12)
13
14# Two ions with softened Coulomb repulsion on the same grid spacing
15H_pair = Hamiltonian.from_mpo(
16    MPO.trapped_ion(
17        positions,
18        masses=[1.0, 1.0],
19        omega=1.0,
20        coulomb_strength=1.0,
21        softening_length=float(positions[1] - positions[0]),
22    )
23)

Pair with State using physical_dimensions=[len(positions)] per ion site. A wavepacket reflection benchmark is in Trapped-Ion Position-Grid Emulation.

Note

YAQS applies \(\exp(-\mathrm{i}\,\Delta t\, H)\) during evolution. When using SI units, pass energies and times in consistent units or rescale \(H/\hbar\) explicitly (see the factory docstring).

Manual Hamiltonians

For imported MPO cores or small-system dense operators:

# MPO tensor cores (rank-4 per site, already in MPO layout)
H = Hamiltonian(tensors=my_cores)

# Dense matrix (MCWF / Lindblad when state is vector or density_matrix)
H = Hamiltonian(matrix=dense_h, physical_dimension=2)