name: tenax-autompo description: > Build Hamiltonian MPOs from natural-language model descriptions using Tenax's AutoMPO. Translates physics ("Heisenberg ladder with NNN coupling and staggered field") into correct AutoMPO code with proper prefactors, site ordering, custom operators, compress and symmetric flags. Use this skill whenever the user wants to build an MPO, construct a Hamiltonian, set up a spin model, define coupling terms, create an operator for DMRG, or asks about AutoMPO, build_auto_mpo, operator terms, Sp Sm Sz conventions, site ordering for cylinders/ladders, or "how do I write the Hamiltonian for [model]". Also trigger for custom operators, NNN interactions, long-range couplings, staggered fields, or multi-site unit cells.
AutoMPO Builder — Hamiltonian Construction for Tenax
You help users build correct MPO Hamiltonians using Tenax's AutoMPO class and
build_auto_mpo functional interface. Your primary job is translating a physical
model description into bug-free AutoMPO code.
Tenax AutoMPO API
Class-based interface
from tenax import AutoMPO
L = 20 # number of sites
auto = AutoMPO(L=L, d=2) # d = local Hilbert space dimension
# Add terms: (coefficient, "Op1", site1, "Op2", site2, ...)
auto += (1.0, "Sz", 0, "Sz", 1) # Two-site term
auto += (0.5, "X", 3) # Single-site term
mpo = auto.to_mpo() # Dense MPO
mpo = auto.to_mpo(symmetric=True) # U(1) block-sparse MPO
mpo = auto.to_mpo(compress=True) # Compressed (for long-range terms)
Functional interface
from tenax import build_auto_mpo
import numpy as np
custom_ops = {
"X": np.array([[0.0, 1.0], [1.0, 0.0]]),
"Z": np.array([[1.0, 0.0], [0.0, -1.0]]),
"Id": np.eye(2),
}
terms = [(1.0, "Z", i, "Z", i + 1) for i in range(L - 1)]
terms += [(0.5, "X", i) for i in range(L)]
mpo = build_auto_mpo(terms, L=L, site_ops=custom_ops)
Built-in operators
Use spin_half_ops() (d=2) and spin_one_ops() (d=3) to get operator dictionaries:
from tenax import spin_half_ops, spin_one_ops
ops = spin_half_ops() # {"Sz", "Sp", "Sm", "Id"}
Spin-1/2 (d=2):
| Name | Matrix | Notes |
|---|---|---|
"Sz" |
diag(1/2, -1/2) | z-component |
"Sp" |
[[0,1],[0,0]] | Raising operator S+ |
"Sm" |
[[0,0],[1,0]] | Lowering operator S- |
"Id" |
eye(2) | Identity |
Spin-1 (d=3):
| Name | Matrix | Notes |
|---|---|---|
"Sz" |
diag(1, 0, -1) | z-component |
"Sp" |
3×3 raising | S+ for spin-1 |
"Sm" |
3×3 lowering | S- for spin-1 |
"Id" |
eye(3) | Identity |
Note: "Sx" and "Sy" are not built-in. If needed, define them as custom
operators or use the Sp/Sm decomposition (recommended to avoid complex numbers).
Translation Rules
When the user describes a model, apply these rules:
Heisenberg exchange: S_i · S_{i+1}
Always decompose as:
auto += (Jz, "Sz", i, "Sz", j)
auto += (Jxy/2, "Sp", i, "Sm", j) # Factor of 0.5!
auto += (Jxy/2, "Sm", i, "Sp", j) # Factor of 0.5!
The 0.5 comes from: S·S = Sz·Sz + (1/2)(S+S- + S-S+). This is the single most common AutoMPO mistake. Always use 0.5 for the Sp/Sm terms unless the user explicitly asks for XX+YY coupling (which would be 1.0 each).
For the isotropic Heisenberg model: Jz = Jxy = J. For XXZ: Jz ≠ Jxy (anisotropic).
Site ordering constraint
AutoMPO requires site indices in ascending order within each term.
For two-site terms, always use min(i,j) and max(i,j):
auto += (J, "Sz", min(i,j), "Sz", max(i,j))
Avoiding complex numbers
Prefer the Sp/Sm decomposition over Sx/Sy. Using "Sy" introduces complex
numbers, which can cause UFuncOutputCastingError with NumPy >= 2.0 and
forces complex128 throughout.
Instead of:
auto += (Jx, "Sx", i, "Sx", j)
auto += (Jy, "Sy", i, "Sy", j)
Use:
auto += (Jz, "Sz", i, "Sz", j)
auto += ((Jx + Jy)/2, "Sp", i, "Sm", j) # = (Jx+Jy)/2 * (S+S- + S-S+)/2...
auto += ((Jx + Jy)/2, "Sm", i, "Sp", j) # Careful: only valid if Jx = Jy
For fully anisotropic XYZ models where Jx ≠ Jy, you may need to use Sx/Sy directly with complex128 dtype, or decompose carefully: Sx·Sx = (1/4)(S+S+ + S+S- + S-S+ + S-S-) Sy·Sy = -(1/4)(S+S+ - S+S- - S-S+ + S-S-)
Geometry mappings
1D chain (open BC):
for i in range(L - 1):
add_bond(i, i + 1)
1D chain (periodic BC):
for i in range(L):
add_bond(i, (i + 1) % L)
Note: periodic BC creates a long-range term (site 0 ↔ site L-1) so use
compress=True.
Ladder (2-leg, open rungs):
# Sites: leg=0 → 0,2,4,... leg=1 → 1,3,5,...
# Rung bond: (2*x, 2*x+1)
# Leg bond: (2*x, 2*(x+1)), (2*x+1, 2*(x+1)+1)
for x in range(Lx):
add_bond(2*x, 2*x + 1) # rung
if x < Lx - 1:
add_bond(2*x, 2*(x+1)) # leg 0
add_bond(2*x+1, 2*(x+1)+1) # leg 1
Cylinder (Lx × Ly, periodic in y, open in x):
Lx, Ly = 6, 3
N = Lx * Ly
for x in range(Lx):
for y in range(Ly):
i = x * Ly + y
j = x * Ly + (y + 1) % Ly # periodic y
add_bond(min(i,j), max(i,j)) # within-ring
if x < Lx - 1:
k = (x + 1) * Ly + y
add_bond(i, k) # between-ring
Flags
symmetric=True— generates U(1) block-sparse MPO. Use when the Hamiltonian conserves total Sz (most spin models without transverse fields). The initial MPS must be in the correct Sz sector.compress=True— compresses the MPO via SVD. Essential for: periodic boundaries, long-range interactions, cylinders with large Ly. Without compression, the MPO bond dimension can be unnecessarily large.
Workflow
- Ask the user to describe the model (or identify it from context).
- Identify: lattice geometry, coupling terms, symmetries, boundary conditions.
- Generate the AutoMPO code with correct prefactors and site ordering.
- Recommend
symmetricandcompressflags based on the model. - Suggest a sanity check: run DMRG on a small system and compare against exact diagonalization or known results.
Common Models Quick Reference
Transverse-field Ising
for i in range(L - 1):
auto += (-J, "Sz", i, "Sz", i + 1)
for i in range(L):
auto += (-h, "Sx", i) # or equivalently: (-h/2, "Sp", i) + (-h/2, "Sm", i)
XXZ model
for i in range(L - 1):
auto += (delta, "Sz", i, "Sz", i + 1)
auto += (0.5, "Sp", i, "Sm", i + 1)
auto += (0.5, "Sm", i, "Sp", i + 1)
Hubbard (d=4: |0⟩, |↑⟩, |↓⟩, |↑↓⟩)
Requires custom operators for creation/annihilation with Jordan-Wigner strings.
Guide the user through defining c_up, c_dn, n_up, n_dn matrices and
using build_auto_mpo with site_ops.
Spin-1 (d=3)
auto = AutoMPO(L=L, d=3) # d=3 for spin-1
# Built-in Sz, Sp, Sm are 3×3 for d=3