Ground State Energy Calculation Issues with LocalHam1D
What is your issue?
Ground State Energy Calculation Issues with LocalHam1D
Problem Summary
I'm trying to calculate the exact ground state energy of a 1D Heisenberg model to compare with my MPS state, but I'm encountering multiple issues with different approaches in quimb.
Environment
- quimb version: 1.11.0
- Python version: 3.10
- Operating system: Linux
- Installation method: conda environment
Code and Errors
Original attempt - missing function
import quimb.tensor as qtn
N = 6
H = qtn.ham_1d_heis(N, j=1.0, cyclic=False)
psi0 = qtn.MPS_rand_state(N, bond_dim=4, dtype='complex128')
energy = psi0.compute_local_expectation(H, max_bond=16)
print("Energia iniziale:", energy.item())
# This fails:
from quimb import exact_gs_energy
E_exact = exact_gs_energy(H)
Error: ImportError: cannot import name 'exact_gs_energy' from 'quimb'
Attempt with heisenberg_energy
from quimb import heisenberg_energy
E_exact = heisenberg_energy(N, j=1.0, cyclic=False)
Error: TypeError: heisenberg_energy() got an unexpected keyword argument 'j'
Attempt with DMRG2
dmrg = qtn.DMRG2(H, bond_dims=[10, 20, 50, 100], cutoffs=1e-12)
dmrg.solve(tol=1e-12, verbosity=1)
E_exact = dmrg.energy
Error:
File "tensor_dmrg.py", line 557, in __init__
self.phys_dim = ham.phys_dim()
AttributeError: 'LocalHam1D' object has no attribute 'phys_dim'
Attempt with matrix conversion
H_dense = H.to_dense() # To use numpy.linalg.eigvals
Error: AttributeError: 'LocalHam1D' object has no attribute 'to_dense'
Questions
-
What is the correct way to calculate the exact ground state energy for a
LocalHam1Dobject created withham_1d_heis()? -
Is
exact_gs_energyavailable in the current version? If not, what's the recommended alternative? -
What's the correct syntax for
heisenberg_energy()? The function exists but parameter names seem different from what I expected. -
Why doesn't DMRG2 work with LocalHam1D? Is there a compatibility issue or do I need to convert the Hamiltonian to a different format?
-
How can I convert a
LocalHam1Dto a dense matrix for exact diagonalization on small systems?
Expected Behavior
I expect to be able to:
- Calculate the exact ground state energy of a small (N=6) Heisenberg chain
- Compare this with the energy of my random MPS state
- Use either analytical formulas, exact diagonalization, or DMRG
Minimal Working Example Needed
Could you provide a minimal working example that shows how to:
import quimb.tensor as qtn
N = 6
H = qtn.ham_1d_heis(N, j=1.0, cyclic=False)
psi = qtn.MPS_rand_state(N, bond_dim=4, dtype='complex128')
energy_mps = psi.compute_local_expectation(H, max_bond=16)
# What should go here to get the exact ground state energy?
energy_exact = ???
print(f"MPS energy: {energy_mps}")
print(f"Exact energy: {energy_exact}")
print(f"Error: {abs(energy_mps - energy_exact)}")
Additional Context
This is for benchmarking MPS optimization algorithms, so having a reliable way to compute exact energies for small systems is crucial. I'm willing to use any approach (analytical, exact diagonalization, or DMRG) as long as it works reliably.
Thank you for your help!
Hi @marcomaronese, I would recommend checking the docs for the answers to most of these if you haven't seen them already. In particular you can search for functions and their docstrings will tell you what arguments they expect.
-
LocalHamobjects are for local_expectations, TEBD and some other PEPS algorithms, they contain a dict of local terms. While you could run some ground state algorithms using this object, if you want an exact energy, it is best to define the hamiltonian some other way. -
exact_gs_energy, not sure where this name come from (AI hallucinated?), but it has never existed. See below for the closest equivalent. -
The doctstring for
heisenberg_energyis here https://quimb.readthedocs.io/en/latest/autoapi/quimb/calc/index.html#quimb.calc.heisenberg_energy. -
Yes DMRG expects an MPO - this part of the intro docs might be a helpful introduction - https://quimb.readthedocs.io/en/latest/tensor-1d.html#quick-dmrg2-intro, there is a built in heisenberg MPO function.
-
LocalHam1Dobjects are like a particular output format of a Hamiltonian, just as an MPO is, rather than the basic operator definition. So while one could write a function to turn it into a matrix, one wouldn't usually do this, instead build the dense/sparse matrix directly.
The main quimb module https://quimb.readthedocs.io/en/latest/index_matrix.html, deals with constructing and calculating things for dense/sparse representations. Here's the simplest way to find an exact energy for a small system:
import quimb as qu
H = qu.ham_heis(6, sparse=True)
qu.groundenergy(H)
# np.float64(-2.4935771338879253)
It is in the experimental submodule still but quimb does have a way to define the operator in an abstract way, and then build out all of the different representations:
import quimb.tensor as qtn
from quimb.experimental import operatorbuilder as qop
edges = qtn.edges_1d_chain(6)
# general definition using heisenberg preset
H = qop.heisenberg_from_edges(edges)
H
# SparseOperatorBuilder(nsites=6, nterms=15, locality=2))
H_dense = H.build_dense()
H_dense.shape
# (64, 64)
# you can use symmetries as well
H_sparse = H.build_sparse_matrix(sector=3, symmetry="U1")
H_sparse, qu.groundenergy(H_sparse)
# (<Compressed Sparse Row sparse matrix of dtype 'float64'
# with 80 stored elements and shape (20, 20)>,
# np.float64(-2.493577133887924))
H_mpo = H.build_mpo()
dmrg = qtn.DMRG2(H_mpo)
dmrg.solve()
H_mpo, dmrg.energy
# (MatrixProductOperator(tensors=6, indices=17, L=6, max_bond=5),
# np.float64(-2.4935771338879253))
# like HamLocal objects:
local_terms = H.build_local_terms()
However, one does need to know what representation is useful for what things.
Thanks for the suggestion to use quimb.experimental, but unfortunately this doesn't work in my case.
Specifically, the method build_sparse_matrix throws the following errors:
TypeError: got an unexpected keyword argument 'sector'
TypeError: got an unexpected keyword argument 'symmetry'
Also, using qu.ham_heis(L, sparse=True) is not suitable either because:
- It returns a dense or sparse
qarray, which is not scalable for large system sizes due to exponential memory usage. - It does not support
.apply_to_arrays(), so I cannot easily convert it to PyTorch tensors for differentiable optimization.
I need to define a single, consistent Heisenberg 1D Hamiltonian that satisfies the following requirements:
- Scalable — usable for variational ground state search with PyTorch as a backend and a large number of sites.
- Exact for small L — allows benchmarking by computing the exact ground state energy for small systems (e.g. $L < 8$).
- Representable as an MPO — usable for tensor network benchmarking (e.g. with DMRG).
I'm trying to use quimb.tensor.MPO_ham_heis, but I encounter the following error:
AttributeError: 'numpy.ndarray' object has no attribute 'permute'
This happens even after setting the backend to PyTorch using set_backend('torch').
Let me know if there's a recommended way to construct 1D ham heis MPO, ideally without relying on LocalHam1D, which currently causes issues when computing the exact ground state, as discussed in this issue.
Thanks!
Specifically, the method build_sparse_matrix throws the following errors:
Ah yes those options are not released yet. You will need to install the development version directly from github, or just remove them. It still will build the matrix representation however, which is exponentially in system size. If you have the dense matrix/array you can just directly convert it to torch, you don't need to call an apply_to_arrays method.
I'm trying to use quimb.tensor.MPO_ham_heis, but I encounter the following error:
I think this is because you are trying to call torch functions on numpy arrays. Generally quimb doesn't set a backend, you just need to make sure the data arrays are converted to the correct lib. For example:
mpo = qtn.MPO_ham_heis(10)
mpo.apply_to_arrays(torch.tensor)
(DMRG is not supported for torch tensors yet however.)
I need to define a single, consistent Heisenberg 1D Hamiltonian that satisfies the following requirements:
I think the operatorbuilder is the closest you are going to get here. The main question is how do you want to compute the energy e.g. for your scalable 'variational ground state search', how are you representing the state?