"""Spin and SU(2)-generator matrix constructors."""
import logging
import numpy as np
from scipy.sparse import block_diag, csr_matrix, diags, identity
from edlgt.tools import validate_parameters
logger = logging.getLogger(__name__)
__all__ = [
"spin_space",
"m_values",
"get_spin_operators",
"get_pauli_operators",
"su2_generators",
"get_Pauli_operators",
"SU2_generators",
]
[docs]
def spin_space(spin):
"""Return the Hilbert-space dimension of a spin irrep."""
validate_parameters(spin_list=[spin])
return int(2 * spin + 1)
[docs]
def m_values(spin):
"""Return the allowed ``m`` quantum numbers for a spin irrep (descending)."""
validate_parameters(spin_list=[spin])
return np.arange(-spin, spin + 1)[::-1]
[docs]
def get_spin_operators(spin):
"""Construct sparse spin matrices for an arbitrary spin representation.
Parameters
----------
spin : scalar
Spin value (integer or half-integer).
Returns
-------
dict
Sparse matrices ``Sz``, ``Sp``, ``Sm``, ``Sx``, ``Sy``, and ``S2``.
"""
validate_parameters(spin_list=[spin])
# Size of the spin matrix
size = spin_space(spin)
shape = (size, size)
# Diagonal entries of the Sz matrix
sz_diag = m_values(spin)
# Diagonal entries of the S+ matrix
sp_diag = np.sqrt(spin * (spin + 1) - sz_diag[1:] * (sz_diag[1:] + 1))
ops = {}
ops["Sz"] = diags(sz_diag, 0, shape)
ops["Sp"] = diags(sp_diag, 1, shape)
ops["Sm"] = ops["Sp"].transpose()
ops["Sx"] = (ops["Sp"] + ops["Sm"]) / 2
ops["Sy"] = complex(0, -0.5) * (ops["Sp"] - ops["Sm"])
ops["S2"] = diags([spin * (spin + 1) for _ in range(size)], 0, shape)
return ops
[docs]
def get_pauli_operators():
"""Return Pauli-operator matrices in the package normalization."""
shape = (2, 2)
ops = {}
ops["I"] = identity(2, dtype=np.float64)
ops["Sz"] = diags(np.array([1.0, -1.0], dtype=np.float64), 0, shape)
ops["Sp"] = diags(np.array([1.0], dtype=np.float64), 1, shape)
ops["Sm"] = ops["Sp"].transpose()
ops["Sx"] = ops["Sp"] + ops["Sm"]
ops["Sy"] = complex(0, -1) * (ops["Sp"] - ops["Sm"])
return ops
[docs]
def su2_generators(spin, matter=False):
"""Construct SU(2) generators for rishon or matter sectors.
Parameters
----------
spin : scalar
Maximum spin irrep used in the block-diagonal construction.
matter : bool, optional
If ``True``, build the generators acting on the matter sector;
otherwise build the rishon-sector generators.
Returns
-------
dict
Dictionary of sparse SU(2) generators and related composites.
"""
validate_parameters(spin_list=[spin], matter=matter)
largest_spin_size = int(2 * spin + 1)
matrices = {"Tz": [0], "Tp": [0], "T2": [0]}
if not matter:
tot_shape = 0
for spin_size in range(1, largest_spin_size):
tot_shape += spin_size
j_spin = spin_size / 2
spin_ops = get_spin_operators(j_spin)
for op in ["z", "p", "2"]:
matrices[f"T{op}"].append(spin_ops[f"S{op}"])
tot_shape += largest_spin_size
matrices["T0"] = np.zeros((tot_shape, tot_shape), dtype=np.float64)
matrices["T0"][0, 0] = 1.0
SU2_gen = {}
SU2_gen["T0"] = csr_matrix(matrices["T0"])
for op in ["Tz", "Tp", "T2"]:
SU2_gen[op] = block_diag(tuple(matrices[op]), format="csr")
SU2_gen["Tm"] = SU2_gen["Tp"].transpose()
SU2_gen["Tx"] = 0.5 * (SU2_gen["Tp"] + SU2_gen["Tm"])
SU2_gen["Ty"] = complex(0, -0.5) * (SU2_gen["Tp"] - SU2_gen["Tm"])
SU2_gen["T4"] = SU2_gen["T2"] ** 2
# Introduce the effective Casimir operator on which a single rishon is acting
gen_size = SU2_gen["T2"].shape[0]
ID = identity(gen_size)
SU2_gen["T2_root"] = 0.5 * (csr_matrix(ID + 4 * SU2_gen["T2"]).sqrt() - ID)
else:
spin_ops = get_spin_operators(1 / 2)
for op in ["z", "p", "2"]:
matrices[f"T{op}"] = [0, spin_ops[f"S{op}"], 0]
SU2_gen = {}
for op in ["z", "p", "2"]:
SU2_gen[f"S{op}_psi"] = block_diag(tuple(matrices[f"T{op}"]), format="csr")
SU2_gen["Sm_psi"] = SU2_gen["Sp_psi"].transpose()
SU2_gen["Sx_psi"] = 0.5 * (SU2_gen["Sp_psi"] + SU2_gen["Sm_psi"])
SU2_gen["Sy_psi"] = complex(0, -0.5) * (SU2_gen["Sp_psi"] - SU2_gen["Sm_psi"])
return SU2_gen
get_Pauli_operators = get_pauli_operators # pylint: disable=invalid-name
SU2_generators = su2_generators # pylint: disable=invalid-name