How to simulate Trotterized time evolution for the Fermi-Hubbard model

In this guide, we show how to use ffsim to simulate Trotterized time evolution for the Fermi-Hubbard model using the split-operator method. In the split-operator method, the Hamiltonian is expressed as a sum of two terms, the one-body part and the two-body part, for the purposes of the Trotter product formula.

First, let’s create a Fermi-Hubbard Hamiltonian and choose the number of electrons, which will remain fixed throughout.

[1]:
import ffsim

norb_x = 4
norb_y = 4

norb = norb_x * norb_y
nelec = (2, 2)
n_alpha, n_beta = nelec

op = ffsim.fermi_hubbard_2d(
    norb_x=norb_x,
    norb_y=norb_y,
    tunneling=1.0,
    interaction=4.0,
    chemical_potential=0.0,
    periodic_x=True,
    periodic_y=False,
)

Before we can simulate Trotterized time evolution, we need to convert the operator to an instance of the DiagonalCoulombHamiltonian class.

[2]:
dc_hamiltonian = ffsim.DiagonalCoulombHamiltonian.from_fermion_operator(op)

Now, let’s apply time evolution to an initial state. Here, we’ll use the ground state of the one-body part of the Hamiltonian as the initial state.

[3]:
import numpy as np

# Construct the initial state.
eigs, orbital_rotation = np.linalg.eigh(dc_hamiltonian.one_body_tensor)
initial_state = ffsim.slater_determinant(
    norb,
    occupied_orbitals=(range(n_alpha), range(n_beta)),
    orbital_rotation=orbital_rotation,
)

# Set the evolution time.
time = 1.0

# Perform the Trotterized time evolution
final_state = ffsim.simulate_trotter_diag_coulomb_split_op(
    initial_state,
    dc_hamiltonian,
    time,
    norb=norb,
    nelec=nelec,
    n_steps=5,
    order=0,
)

To check the error from Trotterization, we can compute the result of exact time evolution and compare it with the final state we got.

[4]:
import scipy.sparse.linalg

# Convert the Hamiltonian to a LinearOperator
linop = ffsim.linear_operator(dc_hamiltonian, norb=norb, nelec=nelec)

# Compute the exact result of time evolution
exact_state = scipy.sparse.linalg.expm_multiply(
    -1j * time * linop,
    initial_state,
    traceA=-1j * time * ffsim.trace(dc_hamiltonian, norb=norb, nelec=nelec),
)

# Compute fidelity between results from Trotterized evolution and exact evolution
fidelity = abs(np.vdot(final_state, exact_state))

print(f"Fidelity of Trotter-evolved state with exact state: {fidelity}")
Fidelity of Trotter-evolved state with exact state: 0.9725640629741702

To reduce the Trotter error, you can increase the number of steps or use a higher-order formula.

[5]:
final_state = ffsim.simulate_trotter_diag_coulomb_split_op(
    initial_state,
    dc_hamiltonian,
    time,
    norb=norb,
    nelec=nelec,
    n_steps=10,
    order=1,
)

fidelity = abs(np.vdot(final_state, exact_state))

print(f"Fidelity of Trotter-evolved state with exact state: {fidelity}")
Fidelity of Trotter-evolved state with exact state: 0.9999380169878249