Source code for ed_lgt.modeling.local_term

import numpy as np
from math import prod
from .lattice_mappings import zig_zag
from .lattice_geometry import get_neighbor_sites
from .qmb_operations import local_op
from .qmb_state import QMB_state
from .qmb_term import QMBTerm
from ed_lgt.tools import validate_parameters
from ed_lgt.symmetries import nbody_term
import logging

logger = logging.getLogger(__name__)

__all__ = ["LocalTerm", "check_link_symmetry"]


[docs] class LocalTerm(QMBTerm): def __init__(self, operator, op_name, **kwargs): """ This function provides methods for computing local Hamiltonian terms in a d-dimensional lattice model. Args: operator (scipy.sparse): A single site sparse operator matrix. op_name (str): Operator name **kwargs: Additional keyword arguments for QMBTerm. """ # Validate type of parameters validate_parameters(op_list=[operator], op_names_list=[op_name]) # Preprocess arguments super().__init__(operator=operator, op_name=op_name, **kwargs)
[docs] def get_Hamiltonian(self, strength, mask=None): """ The function calculates the Local Hamiltonian by summing up local terms for each lattice site, potentially with some sites excluded based on the mask. The result is scaled by the strength parameter before being returned. Args: strength (scalar): Coupling of the Hamiltonian term. mask (np.ndarray, optional): d-dimensional array with bool variables specifying (if True) where to apply the local term. Defaults to None. Raises: TypeError: If the input arguments are of incorrect types or formats. Returns: scipy.sparse: Local Hamiltonian term ready to be used for exact diagonalization/ expectation values. """ # CHECK ON TYPES if not np.isscalar(strength): raise TypeError(f"strength must be SCALAR not a {type(strength)}") # LOCAL HAMILTONIAN H_Local = 0 for ii in range(prod(self.lvals)): coords = zig_zag(self.lvals, ii) # CHECK MASK CONDITION ON THE SITE if self.get_mask_conditions(coords, mask): if self.sector_configs is None: H_Local += local_op(operator=self.op, op_site=ii, **self.def_params) else: # GET ONLY THE SYMMETRY SECTOR of THE HAMILTONIAN TERM H_Local += nbody_term( self.sym_ops, np.array([ii]), self.sector_configs ) return strength * H_Local
[docs] def get_expval(self, psi, stag_label=None): """ The function calculates the expectation value (and it variance) of the Local Hamiltonian and is averaged over all the lattice sites. Args: psi (instance of QMB_state class): QMB state where the expectation value has to be computed stag_label (str, optional): if odd/even, then the expectation value is performed only on that kind of sites. Defaults to None. Raises: TypeError: If the input arguments are of incorrect types or formats. """ # Check on parameters if not isinstance(psi, QMB_state): raise TypeError(f"psi must be instance of class:QMB_state not {type(psi)}") validate_parameters(stag_label=stag_label) # PRINT OBSERVABLE NAME logger.info(f"----------------------------------------------------") ( logger.info(f"{self.op_name}") if stag_label is None else logger.info(f"{self.op_name} {stag_label}") ) # AVERAGE EXP VAL <O> & STD DEVIATION (<O^{2}>-<O>^{2})^{1/2} self.obs = np.zeros(self.lvals) self.avg = 0.0 self.std = 0.0 counter = 0 # RUN OVER THE LATTICE SITES for ii in range(prod(self.lvals)): # Given the 1D point on the d-dimensional lattice, get the corresponding coords coords = zig_zag(self.lvals, ii) # Compute the average value in the site x,y if self.sector_configs is None: exp_obs = psi.expectation_value( local_op(operator=self.op, op_site=ii, **self.def_params) ) # Compute the corresponding quantum fluctuation exp_var = ( psi.expectation_value( local_op(operator=self.op**2, op_site=ii, **self.def_params) ) - exp_obs**2 ) else: # GET THE EXPVAL ON THE SYMMETRY SECTOR exp_obs = psi.expectation_value( nbody_term(self.sym_ops, np.array([ii]), self.sector_configs) ) # Compute the corresponding quantum fluctuation exp_var = ( psi.expectation_value( nbody_term(self.sym_ops, np.array([ii]), self.sector_configs) ** 2 ) - exp_obs**2 ) if self.get_staggered_conditions(coords, stag_label): logger.info(f"{coords} {format(exp_obs, '.12f')}") counter += 1 self.obs[coords] = exp_obs self.avg += exp_obs self.std += exp_var self.avg = self.avg / counter self.std = np.sqrt(np.abs(self.std) / counter) logger.info(f"{format(self.avg, '.10f')} +/- {format(self.std, '.10f')}")
def check_link_symmetry(axis, loc_op1, loc_op2, value=0, sign=1): if not isinstance(loc_op1, LocalTerm): raise TypeError(f"loc_op1 should be instance of LocalTerm, not {type(loc_op1)}") if not isinstance(loc_op2, LocalTerm): raise TypeError(f"loc_op2 should be instance of LocalTerm, not {type(loc_op2)}") for ii in range(prod(loc_op1.lvals)): coords = zig_zag(loc_op1.lvals, ii) coords_list, sites_list = get_neighbor_sites( coords, loc_op1.lvals, axis, loc_op1.has_obc ) if sites_list is None: continue else: c1 = coords_list[0] c2 = coords_list[1] tmp = loc_op1.obs[c1] tmp += sign * loc_op2.obs[c2] if np.abs(tmp - value) > 1e-10: logger.info(f"{loc_op1.obs[c1]}") logger.info(f"{loc_op2.obs[c2]}") raise ValueError(f"{axis}-Link Symmetry is violated at index {ii}") logger.info("----------------------------------------------------") logger.info(f"{axis}-LINK SYMMETRY IS SATISFIED")