import numpy as np
from scipy.sparse import csr_matrix, identity, kron
from ed_lgt.tools import validate_parameters
from .lattice_mappings import zig_zag
from .lattice_geometry import get_site_label
import logging
logger = logging.getLogger(__name__)
__all__ = [
"qmb_operator",
"local_op",
"two_body_op",
"four_body_op",
"construct_operator_list",
"apply_basis_projection",
]
[docs]
def qmb_operator(
ops,
op_names_list,
add_dagger=False,
get_real=False,
get_imag=False,
):
"""
This function performs the QMB operation of an arbitrary long list
of operators of arbitrary dimensions.
Args:
ops (dict): dictionary storing all the single site operators
op_names_list (list): list of the names of the operators involved in the qmb operator
the list is assumed to be stored according to the zig-zag order on the lattice
strength (scalar): real/complex coefficient applied in front of the operator
add_dagger (bool, optional): if true, yields the hermitian conjugate. Defaults to False.
get_real (bool, optional): if true, yields only the real part. Defaults to False.
get_imag (bool, optional): if true, yields only the imaginary part. Defaults to False.
Returns:
csr_matrix: QMB sparse operator
"""
# Validate type of parameters
validate_parameters(
ops_dict=ops,
op_names_list=op_names_list,
add_dagger=add_dagger,
get_real=get_real,
get_imag=get_imag,
)
tmp = ops[op_names_list[0]]
for op in op_names_list[1:]:
tmp = kron(tmp, ops[op])
if add_dagger:
tmp = csr_matrix(tmp + tmp.conj().transpose())
if get_real:
tmp = csr_matrix(tmp + tmp.conj().transpose()) / 2
elif get_imag:
tmp = complex(0.0, -0.5) * (csr_matrix(tmp - tmp.conj().transpose()))
return tmp
[docs]
def local_op(
operator,
op_site,
lvals,
has_obc,
staggered_basis=False,
gauge_basis=None,
):
"""
This function compute the single local operator term on the lattice where the operator
acts on a specific site (the rest is occupied by identities).
Args:
operator (scipy.sparse): A single site sparse operator matrix.
op_site (scalar int): position of the site along a certain 1D ordering in the 2D lattice
lvals (list): Dimensions (# of sites) of a d-dimensional lattice
has_obc (list of bool): true for OBC, false for PBC along each direction
staggered_basis (bool, optional): Whether the lattice has staggered basis. Defaults to False.
gauge_basis (dict, optional): Dictionary of Basis Projectors (sparse matrices) for lattice sites
(corners, borders, lattice core, even/odd sites). Defaults to None.
Raises:
TypeError: If the input arguments are of incorrect types or formats.
Returns:
scipy.sparse.matrix: QMB Hamiltonian in sparse form
"""
# Validate type of parameters
validate_parameters(
op_list=[operator],
op_sites_list=[op_site],
lvals=lvals,
has_obc=has_obc,
staggered_basis=staggered_basis,
gauge_basis=gauge_basis,
)
# Construct the dictionary of operators and their names needed to construct the QMB local term
ops, op_names_list = construct_operator_list(
[operator], [op_site], lvals, has_obc, staggered_basis, gauge_basis
)
return qmb_operator(ops, op_names_list)
[docs]
def two_body_op(
op_list,
op_sites_list,
lvals,
has_obc,
staggered_basis=False,
gauge_basis=None,
):
"""
This function compute the single twobody operator term on the lattice with 2 operators
acting on two specific lattice sites (the rest is occupied by identities).
Args:
op_list (list of 2 scipy.sparse.matrices): list of the 4 operators involved in the Plaquette Term
op_sites_list (list of 2 int): list of the positions of two operators in the 1D chain ordering 2d lattice sites
lvals (list): Dimensions (# of sites) of a d-dimensional lattice
has_obc (list of bool): true for OBC, false for PBC along each direction
staggered_basis (bool, optional): Whether the lattice has staggered basis. Defaults to False.
gauge_basis (dict, optional): Dictionary of Basis Projectors (sparse matrices) for lattice sites
(corners, borders, lattice core, even/odd sites). Defaults to None.
Raises:
TypeError: If the input arguments are of incorrect types or formats.
Returns:
scipy.sparse.matrix: QMB Hamiltonian in sparse form
"""
# Validate type of parameters
validate_parameters(
op_list=op_list,
op_sites_list=op_sites_list,
lvals=lvals,
has_obc=has_obc,
staggered_basis=staggered_basis,
gauge_basis=gauge_basis,
)
# Construct the dictionary of operators and their names needed to construct the QMB twobody term
ops, op_names_list = construct_operator_list(
op_list, op_sites_list, lvals, has_obc, staggered_basis, gauge_basis
)
return qmb_operator(ops, op_names_list)
[docs]
def four_body_op(
op_list,
op_sites_list,
lvals,
has_obc,
staggered_basis=False,
gauge_basis=None,
get_real=False,
):
"""
This function compute the single plaquette operator term on the lattice with 4 operators
acting on 4 specific lattice sites (the rest is occupied by identities).
Args:
op_list (list of 4 scipy.sparse.matrices): list of the 4 operators involved in the Plaquette Term
op_sites_list (list of 4 int): list of the positions of two operators in the 1D chain ordering 2d lattice sites
lvals (list): Dimensions (# of sites) of a d-dimensional lattice
has_obc (list of bool): true for OBC, false for PBC along each direction
staggered_basis (bool, optional): Whether the lattice has staggered basis. Defaults to False.
gauge_basis (dict, optional): Dictionary of Basis Projectors (sparse matrices) for lattice sites
(corners, borders, lattice core, even/odd sites). Defaults to None.
get_real (bool, optional): If true, it yields the real part of the operator. Defaults to False.
Raises:
TypeError: If the input arguments are of incorrect types or formats.
Returns:
scipy.sparse.matrix: QMB Hamiltonian in sparse form
"""
# Validate type of parameters
validate_parameters(
op_list=op_list,
op_sites_list=op_sites_list,
lvals=lvals,
has_obc=has_obc,
get_real=get_real,
staggered_basis=staggered_basis,
gauge_basis=gauge_basis,
)
# Construct the dictionary of operators and their names needed to construct the QMB twobody term
ops, op_names_list = construct_operator_list(
op_list, op_sites_list, lvals, has_obc, staggered_basis, gauge_basis
)
return qmb_operator(ops, op_names_list, get_real=get_real)
[docs]
def construct_operator_list(
op_list, op_sites_list, lvals, has_obc, staggered_basis, gauge_basis
):
"""
Constructs a dictionary of operators and a list of their names for a quantum many-body lattice.
Each operator is placed at specified positions on the lattice, and its basis is projected according to the site's characteristics.
Args:
op_list (list of scipy.sparse matrices): Single-site operators to be placed on the lattice.
op_sites_list (list of ints): Indices in the lattice where each operator from 'operators' should be placed.
lvals (list of ints): Dimensions of the lattice, representing the number of sites in each dimension.
has_obc (list of bool): true for OBC, false for PBC along each direction
staggered_basis (bool): Indicates if a staggered basis is used for the lattice.
gauge_basis (dict): A dictionary containing the basis projectors for each site, keyed by site labels.
Returns:
tuple: A tuple containing a dictionary of operators keyed by their names and a list of operator names.
"""
# Validate type of parameteres
validate_parameters(
op_list=op_list,
op_sites_list=op_sites_list,
lvals=lvals,
has_obc=has_obc,
staggered_basis=staggered_basis,
gauge_basis=gauge_basis,
)
all_sites_equal = True if gauge_basis is None else False
# Define the identity operator
ID = identity(op_list[0].shape[0])
# Empty dictionary of operators and list of their names
ops_dict = {}
op_names_list = []
# Assign a name to the operator at position ii
tmp = 0
for ii in range(np.prod(lvals)):
if ii in op_sites_list:
tmp += 1
op_name = f"op{tmp}"
op = op_list[op_sites_list.index(ii)]
else:
op_name = "ID"
op = ID
# Get the coordinates of ii in the d-dim lattice
coords = zig_zag(lvals, ii)
# Get the site label according to its position in the lattice (border, corner, core)
basis_label = get_site_label(
coords, lvals, has_obc, staggered_basis, all_sites_equal
)
# Apply projection of the operator on the proper basis for that lattice position (and update its name)
op, op_name = apply_basis_projection(op, op_name, basis_label, gauge_basis)
# Save operator and its name
ops_dict[op_name] = op
op_names_list.append(op_name)
return ops_dict, op_names_list
[docs]
def apply_basis_projection(op, op_name, basis_label, gauge_basis):
"""
Applies basis projection to an operator for a specific lattice site and updates its name.
Args:
op (scipy.sparse matrix): The operator to be projected.
op_name (str): The name of the operator.
basis_label (str): The label identifying the basis projection applicable to the lattice site.
gauge_basis (dict of scipy.sparse matrices): Dictionary containing the basis projectors for each site, keyed by site labels.
Returns:
tuple: A tuple containing the projected operator and its updated name.
"""
# Validate type of parameteres
validate_parameters(
op_list=[op],
op_names_list=[op_name],
gauge_basis=gauge_basis,
)
if len(basis_label) > 0 and gauge_basis is not None:
# Apply projection of the operator on the proper basis for that lattice position
op = gauge_basis[basis_label].transpose() * op * gauge_basis[basis_label]
# Update the name of the operator with the label
op_name = f"{op_name}_{basis_label}"
return op, op_name