"""Z2 Fermi-Hubbard lattice gauge model."""
import logging
import numpy as np
from edlgt.modeling import (
LocalTerm,
NBodyTerm,
TwoBodyTerm,
border_mask,
check_link_symmetry,
)
from edlgt.operators import (
Z2_FermiHubbard_dressed_site_operators,
Z2_FermiHubbard_gauge_invariant_states,
)
from .quantum_model import QuantumModel
logger = logging.getLogger(__name__)
__all__ = ["Z2_FermiHubbard_Model"]
[docs]
class Z2_FermiHubbard_Model(QuantumModel):
"""Z2 Fermi-Hubbard model with gauge-invariant local basis reduction."""
def __init__(self, sectors, ham_format, **kwargs):
"""Initialize the Z2 Fermi-Hubbard model.
Parameters
----------
sectors : list
Global symmetry-sector labels ``[N_tot, N_up]``.
ham_format : str
Hamiltonian representation format (e.g. ``"sparse"``).
**kwargs
Arguments forwarded to :class:`~edlgt.models.quantum_model.QuantumModel`.
"""
# Initialize base class with the common parameters
super().__init__(**kwargs)
self.ham_format = ham_format
# -------------------------------------------------------------------------------
# Acquire gauge-invariant basis and operators
ops = Z2_FermiHubbard_dressed_site_operators(lattice_dim=self.dim)
self.gauge_basis, self.gauge_states = Z2_FermiHubbard_gauge_invariant_states(
lattice_dim=self.dim
)
# Initialize the operators, local dimension and lattice labels
self.project_operators(ops)
# -------------------------------------------------------------------------------
# GLOBAL SYMMETRIES
global_ops = [self.ops["N_tot"], self.ops["N_up"]]
global_sectors = sectors
# -------------------------------------------------------------------------------
# LINK SYMMETRIES
link_ops = [
[self.ops[f"n_p{d}"], -self.ops[f"n_m{d}"]] for d in self.directions
]
link_sectors = [0 for _ in self.directions]
# GET SYMMETRY SECTOR
self.get_abelian_symmetry_sector(
global_ops=global_ops,
global_sectors=global_sectors,
link_ops=link_ops,
link_sectors=link_sectors,
)
# DEFAULT PARAMS
self.default_params()
[docs]
def build_Hamiltonian(self, coeffs):
"""Assemble the Z2 Fermi-Hubbard Hamiltonian.
Parameters
----------
coeffs : dict
Coupling dictionary with keys ``"U"`` (on-site Coulomb interaction),
``"t"`` (hopping amplitude), ``"h"`` (external electric field),
and ``"J"`` (string tension, used only under PBC along x).
"""
logger.info("----------------------------------------------------")
logger.info("BUILDING HAMILTONIAN")
# Hamiltonian Coefficients
self.coeffs = coeffs
h_terms = {}
# -------------------------------------------------------------------------------
# COULOMB POTENTIAL
op_name = "N_pair_half"
h_terms["V"] = LocalTerm(self.ops[op_name], op_name, **self.def_params)
self.H.add_term(h_terms["V"].get_Hamiltonian(strength=self.coeffs["U"]))
# -------------------------------------------------------------------------------
# HOPPING
for d in self.directions:
for s in ["up", "down"]:
op_names_list = [f"Q{s}_p{d}_dag", f"Q{s}_m{d}"]
op_list = [self.ops[op] for op in op_names_list]
h_terms[f"{d}_hop_{s}"] = TwoBodyTerm(
d, op_list, op_names_list, **self.def_params
)
self.H.add_term(
h_terms[f"{d}_hop_{s}"].get_Hamiltonian(
strength=self.coeffs["t"], add_dagger=True
)
)
# -------------------------------------------------------------------------------
# EXTERNAL ELECTRIC FIELD
for ii, d in enumerate(self.directions):
border = f"p{d}"
op_name = f"P_{border}"
if self.has_obc[ii]:
# Apply the mask on the specific border
mask = ~border_mask(self.lvals, border)
else:
mask = None
h_terms[op_name] = LocalTerm(self.ops[op_name], op_name, **self.def_params)
self.H.add_term(
h_terms[op_name].get_Hamiltonian(strength=self.coeffs["h"], mask=mask)
)
# -------------------------------------------------------------------------------
# STRING Z OPERATOR (only for PBC along x)
if not self.has_obc[0]:
op_names_list = ["Sz_mxpx" for _ in range(self.lvals[0])]
op_list = [self.ops[op] for op in op_names_list]
distances = [[ii, 0] for ii in range(1, self.lvals[0], 1)]
mask = np.zeros(tuple(self.lvals), dtype=bool)
for ii in range(self.lvals[1]):
mask[0, ii] = True
h_terms["Zflux"] = NBodyTerm(
op_list, op_names_list, distances, **self.def_params
)
self.H.add_term(
h_terms["Zflux"].get_Hamiltonian(strength=-self.coeffs["J"], mask=mask)
)
# -------------------------------------------------------------------------------
self.H.build(self.ham_format)
[docs]
def check_symmetries(self):
"""Check link-symmetry constraints on measured observables."""
# CHECK LINK SYMMETRIES
for ax in self.directions:
check_link_symmetry(
ax,
self.obs_list[f"n_p{ax}"],
self.obs_list[f"n_m{ax}"],
value=0,
sign=-1,
)