Skip to content

Commit 8caf8e0

Browse files
committed
feat: implement Heisenberg and Rydberg Hamiltonian generation
1 parent 1d3ef70 commit 8caf8e0

File tree

2 files changed

+139
-12
lines changed

2 files changed

+139
-12
lines changed

tensorcircuit/templates/hamiltonians.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import tensorcircuit as tc
1+
import typing
2+
from typing import cast
23
from scipy.sparse import coo_matrix
34
import numpy as np
5+
import tensorcircuit as tc
46

5-
import typing
67

78
from .lattice import AbstractLattice
89

@@ -57,13 +58,9 @@ def generate_heisenberg_hamiltonian(
5758
ls.append(zz_string)
5859
weights.append(j_coupling)
5960

60-
hamiltonian_matrix = tc.quantum.PauliStringSum2COO(
61-
ls, weight=weights, numpy=True
62-
)
63-
64-
return hamiltonian_matrix
65-
61+
hamiltonian_matrix = tc.quantum.PauliStringSum2COO(ls, weight=weights, numpy=True)
6662

63+
return cast(coo_matrix ,hamiltonian_matrix)
6764

6865

6966
def generate_rydberg_hamiltonian(
@@ -139,8 +136,6 @@ def generate_rydberg_hamiltonian(
139136
ls.append(z_string)
140137
weights.append(float(z_coefficients[i]))
141138

142-
hamiltonian_matrix = tc.quantum.PauliStringSum2COO(
143-
ls, weight=weights, numpy=True
144-
)
139+
hamiltonian_matrix = tc.quantum.PauliStringSum2COO(ls, weight=weights, numpy=True)
145140

146-
return hamiltonian_matrix
141+
return cast(coo_matrix,hamiltonian_matrix)

tests/test_hamiltonians.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import pytest
2+
import numpy as np
3+
4+
from tensorcircuit.templates.lattice import (
5+
ChainLattice,
6+
SquareLattice,
7+
CustomizeLattice,
8+
)
9+
from tensorcircuit.templates.hamiltonians import (
10+
generate_heisenberg_hamiltonian,
11+
generate_rydberg_hamiltonian,
12+
)
13+
14+
PAULI_X = np.array([[0, 1], [1, 0]], dtype=complex)
15+
PAULI_Y = np.array([[0, -1j], [1j, 0]], dtype=complex)
16+
PAULI_Z = np.array([[1, 0], [0, -1]], dtype=complex)
17+
PAULI_I = np.eye(2, dtype=complex)
18+
19+
20+
class TestHeisenbergHamiltonian:
21+
"""
22+
Test suite for the generate_heisenberg_hamiltonian function.
23+
"""
24+
25+
def test_empty_lattice(self):
26+
"""
27+
Test that an empty lattice produces a 0x0 matrix.
28+
"""
29+
empty_lattice = CustomizeLattice(
30+
dimensionality=2, identifiers=[], coordinates=[]
31+
)
32+
h = generate_heisenberg_hamiltonian(empty_lattice)
33+
assert h.shape == (0, 0)
34+
assert h.nnz == 0
35+
36+
def test_single_site(self):
37+
"""
38+
Test that a single-site lattice (no bonds) produces a 2x2 zero matrix.
39+
"""
40+
single_site_lattice = ChainLattice(size=(1,), pbc=False)
41+
h = generate_heisenberg_hamiltonian(single_site_lattice)
42+
assert h.shape == (2, 2)
43+
assert h.nnz == 0
44+
45+
def test_two_sites_chain(self):
46+
"""
47+
Test a two-site chain against a manually calculated Hamiltonian.
48+
This is the most critical test for scientific correctness.
49+
"""
50+
lattice = ChainLattice(size=(2,), pbc=False)
51+
j_coupling = -1.5 # Test with a non-trivial coupling constant
52+
h_generated = generate_heisenberg_hamiltonian(lattice, j_coupling=j_coupling)
53+
54+
# Manually construct the expected Hamiltonian: H = J * (X_0X_1 + Y_0Y_1 + Z_0Z_1)
55+
xx = np.kron(PAULI_X, PAULI_X)
56+
yy = np.kron(PAULI_Y, PAULI_Y)
57+
zz = np.kron(PAULI_Z, PAULI_Z)
58+
h_expected = j_coupling * (xx + yy + zz)
59+
60+
assert h_generated.shape == (4, 4)
61+
assert np.allclose(h_generated.toarray(), h_expected)
62+
63+
def test_square_lattice_properties(self):
64+
"""
65+
Test properties of a larger lattice (2x2 square) without full matrix comparison.
66+
"""
67+
lattice = SquareLattice(size=(2, 2), pbc=True) # 4 sites, 8 bonds with PBC
68+
h = generate_heisenberg_hamiltonian(lattice, j_coupling=1.0)
69+
70+
assert h.shape == (16, 16)
71+
assert h.nnz > 0
72+
h_dense = h.toarray()
73+
assert np.allclose(h_dense, h_dense.conj().T)
74+
75+
76+
class TestRydbergHamiltonian:
77+
"""
78+
Test suite for the generate_rydberg_hamiltonian function.
79+
"""
80+
81+
def test_single_site_rydberg(self):
82+
"""
83+
Test a single atom, which should only have driving and detuning terms.
84+
"""
85+
lattice = ChainLattice(size=(1,), pbc=False)
86+
omega, delta, c6 = 2.0, 0.5, 100.0
87+
h_generated = generate_rydberg_hamiltonian(lattice, omega, delta, c6)
88+
89+
h_expected = (omega / 2.0) * PAULI_X + (delta / 2.0) * PAULI_Z
90+
91+
assert h_generated.shape == (2, 2)
92+
assert np.allclose(h_generated.toarray(), h_expected)
93+
94+
def test_two_sites_rydberg(self):
95+
"""
96+
Test a two-site chain for Rydberg Hamiltonian, including interaction.
97+
"""
98+
lattice = ChainLattice(size=(2,), pbc=False, lattice_constant=1.5)
99+
omega, delta, c6 = 1.0, -0.5, 10.0
100+
h_generated = generate_rydberg_hamiltonian(lattice, omega, delta, c6)
101+
102+
v_ij = c6 / (1.5**6)
103+
104+
h1 = (omega / 2.0) * (np.kron(PAULI_X, PAULI_I) + np.kron(PAULI_I, PAULI_X))
105+
z0_coeff = delta / 2.0 - v_ij / 4.0
106+
z1_coeff = delta / 2.0 - v_ij / 4.0
107+
h2 = z0_coeff * np.kron(PAULI_Z, PAULI_I) + z1_coeff * np.kron(PAULI_I, PAULI_Z)
108+
h3 = (v_ij / 4.0) * np.kron(PAULI_Z, PAULI_Z)
109+
110+
h_expected = h1 + h2 + h3
111+
112+
assert h_generated.shape == (4, 4)
113+
assert np.allclose(h_generated.toarray(), h_expected)
114+
115+
def test_zero_distance_robustness(self):
116+
"""
117+
Test that the function does not crash when two atoms have zero distance.
118+
"""
119+
lattice = CustomizeLattice(
120+
dimensionality=2,
121+
identifiers=[0, 1],
122+
coordinates=[[0.0, 0.0], [0.0, 0.0]],
123+
)
124+
125+
try:
126+
h = generate_rydberg_hamiltonian(lattice, omega=1.0, delta=1.0, c6=1.0)
127+
# The X terms contribute 8 non-zero elements.
128+
# The Z terms (Z0+Z1) have diagonal elements that cancel out,
129+
# resulting in only 2 non-zero elements. Total nnz = 8 + 2 = 10.
130+
assert h.nnz == 10
131+
except ZeroDivisionError:
132+
pytest.fail("The function failed to handle zero distance between sites.")

0 commit comments

Comments
 (0)