Source code for ed_lgt.tools.checks

"""
This module provides utility functions for manipulating quantum many-body operators and matrices.
"""

import numpy as np
from functools import wraps
from math import prod
from scipy.sparse import isspmatrix
from scipy.sparse.linalg import norm
from time import perf_counter
import logging

logger = logging.getLogger(__name__)

__all__ = [
    "validate_parameters",
    "pause",
    "alert",
    "commutator",
    "anti_commutator",
    "check_commutator",
    "check_matrix",
    "check_hermitian",
    "get_time",
]


[docs] def get_time(func): """Times any function""" @wraps(func) def wrapper(*args, **kwargs): start_time = perf_counter() result = func(*args, **kwargs) end_time = perf_counter() tot_time = end_time - start_time logger.debug(f"TIME {func.__name__} {round(tot_time, 5)}") return result return wrapper
[docs] def validate_parameters( lvals=None, loc_dims=None, lattice_dim=None, has_obc=None, axes=None, site_label=None, coords=None, ops_dict=None, op_list=None, op_names_list=None, op_sites_list=None, add_dagger=None, get_real=None, get_imag=None, staggered_basis=None, stag_label=None, all_sites_equal=None, gauge_basis=None, dictionary=None, filename=None, phrase=None, debug=None, psi=None, spmatrix=None, index=None, threshold=None, print_plaq=None, spin_list=None, int_list=None, sz_list=None, pure_theory=None, matter=None, psi_vacuum=None, get_singlet=None, ): """ This is a function for type validation of parameters widely used in the library """ # ----------------------------------------------------------------------------- if lvals is not None and ( not isinstance(lvals, list) or not all(isinstance(x, int) for x in lvals) ): raise TypeError(f"lvals should be a LIST of INTs, not {type(lvals)}") if lattice_dim is not None and not isinstance(lattice_dim, int): raise TypeError(f"lattice_dim should be INT, not {type(lattice_dim)}") if loc_dims is not None: if isinstance(loc_dims, int): loc_dims = np.full(prod(lvals), loc_dims) elif isinstance(loc_dims, list): loc_dims = np.asarray(loc_dims) elif not isinstance(loc_dims, np.ndarray): raise TypeError( f"loc_dims must be INT, LIST, or np.ndarray, not {type(loc_dims)}" ) if has_obc is not None and ( not isinstance(has_obc, list) or not all(isinstance(x, bool) for x in has_obc) ): raise TypeError(f"has_obc should be a LIST of BOOLs, not {type(has_obc)}") if axes is not None and ( not isinstance(axes, list) or not all(isinstance(ax, str) for ax in axes) ): raise TypeError(f"axes should be a LIST of STRs, not {type(axes)}") if site_label is not None and not isinstance(site_label, str): raise TypeError(f"site_label should be a STRING, not {type(site_label)}") if coords is not None and not ( (isinstance(coords, (tuple, list)) and all(isinstance(x, int) for x in coords)) ): raise TypeError(f"coords must be a TUPLE or LIST of INTs, not {type(coords)}") # ----------------------------------------------------------------------------- if ops_dict is not None and not isinstance(ops_dict, dict): raise TypeError(f"ops_dict must be a DICT, not {type(ops_dict)}") if op_list is not None and not any( [ isinstance(axes, list), any( [ all([isspmatrix(op) for op in op_list]), all([isinstance(op, np.ndarray) for op in op_list]), ] ), ] ): raise TypeError( f"op_list must be a LIST of SPARSE/Numpy matrices, not {type(op_list)}" ) if op_sites_list is not None and ( not isinstance(op_sites_list, list) or not all(isinstance(x, int) for x in op_sites_list) ): raise TypeError( f"op_sites_list must be a LIST of INTs, not {type(op_sites_list)}" ) if op_names_list is not None and ( not isinstance(op_names_list, list) or not all(isinstance(x, str) for x in op_names_list) ): raise TypeError( f"op_names_list must be a LIST of INTs, not {type(op_names_list)}" ) # ----------------------------------------------------------------------------- if add_dagger is not None and not isinstance(add_dagger, bool): raise TypeError(f"add_dagger should be a BOOL, not {type(add_dagger)}") if get_real is not None and not isinstance(get_real, bool): raise TypeError(f"get_real should be a BOOL, not {type(get_real)}") if get_imag is not None and not isinstance(get_imag, bool): raise TypeError(f"get_imag should be a BOOL, not {type(get_imag)}") # ----------------------------------------------------------------------------- if staggered_basis is not None and not isinstance(staggered_basis, bool): raise TypeError(f"staggered_basis must be a BOOL, not {type(staggered_basis)}") if stag_label is not None and not any([stag_label == "even", stag_label == "odd"]): raise ValueError(f"stag_label must be 'even' or 'odd', not {stag_label}") if all_sites_equal is not None and not isinstance(all_sites_equal, bool): raise TypeError( f"all_sites_equal should be a BOOL, not {type(all_sites_equal)}" ) if gauge_basis is not None and not isinstance(gauge_basis, dict): raise TypeError(f"gauge_basis must be a DICT, not {type(gauge_basis)}") # ----------------------------------------------------------------------------- if dictionary is not None and not isinstance(dictionary, dict): raise TypeError(f"dictionary should be a DICT, not {type(dictionary)}") if filename is not None and not isinstance(filename, str): raise TypeError(f"filename should be a STRING, not {type(filename)}") # ----------------------------------------------------------------------------- if phrase is not None and not isinstance(phrase, str): raise TypeError(f"phrase should be a STRING, not {type(phrase)}") if debug is not None and not isinstance(debug, bool): raise TypeError(f"debug should be a BOOL, not {type(debug)}") # ----------------------------------------------------------------------------- if psi is not None and not isinstance(psi, np.ndarray): raise TypeError(f"psi should be an ndarray, not a {type(psi)}") # ----------------------------------------------------------------------------- if spmatrix is not None and not isspmatrix(spmatrix): raise TypeError(f"spmatrix should be a BOOL, not {type(spmatrix)}") if index is not None and not isinstance(index, int): raise TypeError(f"index should be a SCALAR INT, not {type(index)}") if threshold is not None and not isinstance(threshold, float): raise TypeError(f"threshold should be a SCALAR FLOAT, not {type(threshold)}") # ----------------------------------------------------------------------------- if print_plaq is not None and not isinstance(print_plaq, bool): raise TypeError(f"print_plaq must be a BOOL, not a {type(print_plaq)}") # ----------------------------------------------------------------------------- # List of spin irreps if spin_list is not None: if not isinstance(spin_list, list): raise TypeError(f"spin_list must be a list, not {type(spin_list)}") else: for ii, spin in enumerate(spin_list): if not float(2 * spin).is_integer() or spin < 0: raise TypeError( f"The {ii} spin must be positive (half-)integer, not {spin}" ) # n values for the Zn group if int_list is not None and ( not isinstance(int_list, list) or not all(isinstance(n, int) for n in int_list) ): raise TypeError(f"int_list must be a list of integers, not {int_list}") # 3rd components of spins if sz_list is not None: if not isinstance(sz_list, list): raise TypeError(f"sz_list must be a list, not {type(sz_list)}") else: for ii, sz in enumerate(sz_list): if not float(2 * sz).is_integer(): raise TypeError( f"The {ii} z-component must be (half-)integer, not {sz}" ) if pure_theory is not None and not isinstance(pure_theory, bool): raise TypeError(f"pure_theory must be BOOL, not {type(pure_theory)}") if matter is not None and not isinstance(matter, bool): raise TypeError(f"matter must be BOOL, not {type(matter)}") if psi_vacuum is not None and not isinstance(psi_vacuum, bool): raise TypeError(f"psi_vacuum must be bool, not {type(psi_vacuum)}") if get_singlet is not None and not isinstance(get_singlet, bool): raise TypeError(f"get_singlet must be bool, not {type(get_singlet)}")
[docs] def pause(phrase, debug): """ Pause the execution of the program and display a message. Args: phrase (str): The message to display. debug (bool): If ``True``, the pause and message are executed; if ``False``, they are skipped. Raises: TypeError: If the input arguments are of incorrect types or formats. """ # Validate type of parameters validate_parameters(phrase=phrase, debug=debug) if debug == True: # IT PROVIDES A PAUSE in a given point of the PYTHON CODE logger.debug("----------------------------------------------------") # Press the <ENTER> key to continue programPause = input(phrase) logger.debug("----------------------------------------------------") logger.debug("")
[docs] def alert(phrase, debug): """ Display an alert message during program execution. Args: phrase (str): The alert message to display. debug (bool): If ``True``, the alert and message are executed; if ``False``, they are skipped. Raises: TypeError: If the input arguments are of incorrect types or formats. """ # Validate type of parameters validate_parameters(phrase=phrase, debug=debug) if debug == True: # IT PRINTS A PHRASE IN A GIVEN POINT OF A PYTHON CODE logger.debug("") logger.debug(phrase)
[docs] def commutator(A, B): """ Compute the commutator of two sparse matrices. Args: A (scipy.sparse.csr_matrix): First matrix B (scipy.sparse.csr_matrix): Second matrix Raises: TypeError: If the input arguments are of incorrect types or formats. Returns: scipy.sparse.csr_matrix: The commutator of matrices A and B. """ validate_parameters(spmatrix=A) validate_parameters(spmatrix=B) return A * B - B * A
[docs] def anti_commutator(A, B): """ Compute the anti-commutator of two sparse matrices. Args: A (scipy.sparse.csr_matrix): First matrix B (scipy.sparse.csr_matrix): Second matrix Raises: TypeError: If the input arguments are of incorrect types or formats. Returns: scipy.sparse.csr_matrix: The anti-commutator of matrices A and B. """ validate_parameters(spmatrix=A) validate_parameters(spmatrix=B) return A * B + B * A
[docs] def check_commutator(A, B): """ Check the commutation relations between two operators A and B. Args: A (scipy.sparse.csr_matrix): First matrix B (scipy.sparse.csr_matrix): Second matrix Raises: TypeError: If the input arguments are of incorrect types or formats. ValueError: If the commutation ratio is greater than a threshold. """ # CHECKS THE COMMUTATION RELATIONS BETWEEN THE OPERATORS A AND B validate_parameters(spmatrix=A) validate_parameters(spmatrix=B) norma = norm(A * B - B * A) norma_max = max(norm(A * B + B * A), norm(A), norm(B)) ratio = norma / norma_max # check=(AB!=BA).nnz if ratio > 10 ** (-15): logger.debug(" ERROR: A and B do NOT COMMUTE") logger.debug(" NORM", norma) logger.debug(" RATIO", ratio) logger.debug("")
[docs] def check_matrix(A, B): """ Check the difference between two sparse matrices A and B computing the Frobenius Norm Args: A (scipy.sparse.csr_matrix): First matrix B (scipy.sparse.csr_matrix): Second matrix Raises: TypeError: If the input arguments are of incorrect types or formats. ValueError: If the matrices have different shapes or the difference ratio is above a threshold. """ # CHEKS THE DIFFERENCE BETWEEN TWO SPARSE MATRICES validate_parameters(spmatrix=A) validate_parameters(spmatrix=B) if A.shape != B.shape: raise ValueError(f"Shape mismatch between : A {A.shape} & B: {B.shape}") norma = norm(A - B) norma_max = max(norm(A + B), norm(A), norm(B)) ratio = norma / norma_max if ratio > 1e-15: logger.debug(" ERROR: A and B are DIFFERENT MATRICES") raise ValueError(f" NORM {norma}, RATIO {ratio}")
[docs] def check_hermitian(A): """ Check if a sparse matrix A is Hermitian. Args: A (scipy.sparse.csr_matrix): The sparse matrix to check for Hermiticity. Raises: TypeError: If the input matrix is not in the correct format. """ validate_parameters(spmatrix=A) A_dag = A.getH() check_matrix(A, A_dag) # Get the Hermitian logger.debug("HERMITICITY VALIDATED")