quimb icon indicating copy to clipboard operation
quimb copied to clipboard

Ground State Energy Calculation Issues with LocalHam1D

Open marcomaronese opened this issue 7 months ago • 3 comments

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

  1. What is the correct way to calculate the exact ground state energy for a LocalHam1D object created with ham_1d_heis()?

  2. Is exact_gs_energy available in the current version? If not, what's the recommended alternative?

  3. What's the correct syntax for heisenberg_energy()? The function exists but parameter names seem different from what I expected.

  4. Why doesn't DMRG2 work with LocalHam1D? Is there a compatibility issue or do I need to convert the Hamiltonian to a different format?

  5. How can I convert a LocalHam1D to 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!

marcomaronese avatar Jun 13 '25 13:06 marcomaronese

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.

  1. LocalHam objects 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.

  2. exact_gs_energy, not sure where this name come from (AI hallucinated?), but it has never existed. See below for the closest equivalent.

  3. The doctstring for heisenberg_energy is here https://quimb.readthedocs.io/en/latest/autoapi/quimb/calc/index.html#quimb.calc.heisenberg_energy.

  4. 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.

  5. LocalHam1D objects 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.

jcmgray avatar Jun 13 '25 18:06 jcmgray

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:

  1. It returns a dense or sparse qarray, which is not scalable for large system sizes due to exponential memory usage.
  2. 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:

  1. Scalable — usable for variational ground state search with PyTorch as a backend and a large number of sites.
  2. Exact for small L — allows benchmarking by computing the exact ground state energy for small systems (e.g. $L < 8$).
  3. 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!

marcomaronese avatar Jun 18 '25 09:06 marcomaronese

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?

jcmgray avatar Jun 18 '25 19:06 jcmgray