Skip to content

spectralgap

qemcmc.spectralgap

SpectralGap

SpectralGap(proposal, model, temp=1.0)

Bases: Runner

Class that finds the spectral gap, and the acceptance and proposal matrices for a given mcmc.

Source code in src/qemcmc/spectralgap.py
def __init__(self, proposal: Proposal, model: EnergyModel, temp: float = 1.0):
    self.proposal = proposal
    self.model = model
    self.temp = temp

find_acceptance_matrix

find_acceptance_matrix()

Function to find the acceptance matrix for a given model instance.

Returns: A (np.ndarray): The acceptance matrix for the mcmc

Source code in src/qemcmc/spectralgap.py
def find_acceptance_matrix(self):
    """
    Function to find the acceptance matrix for a given model instance.

    Returns:
        A (np.ndarray): The acceptance matrix for the mcmc

    """

    num_states = 2**self.proposal.n_spins

    A = np.zeros((num_states, num_states))

    energies = self.model.get_all_energies()

    for i in range(num_states):
        for j in range(num_states):
            if i != j:
                A[i][j] = self.get_acceptance_probability(energies[i], energies[j], temperature=self.temp)
            else:
                A[i][j] = 0

    return A

find_proposal_matrix_local

find_proposal_matrix_local()

Function to find the proposal matrix for a given local chain.

Returns: Q (np.ndarray): The Q matrix for local proposal

Source code in src/qemcmc/spectralgap.py
def find_proposal_matrix_local(self):
    """
    Function to find the proposal matrix for a given local chain.

    Returns:
        Q (np.ndarray): The Q matrix for local proposal
    """

    possible_states = self.model.S
    # TODO: define S separately, not inside the model since S is the state space and will be pretty inefficient otherwise

    Q = np.zeros((2**self.model.n_spins, 2**self.model.n_spins))

    # loop throguh and find the difference in bitstrings.
    # When the ith bitstring is different (by a valua of 1) from the jth bitstring add 1 to Q[i,j]
    for i in range(2**self.model.n_spins):
        for j in range(2**self.model.n_spins):
            # TODO: what's sm? maybe rename to something like diff_count or something descriptive
            sm = 0
            for k in range(self.model.n_spins):
                sm += abs(int(possible_states[i][k]) - int(possible_states[j][k]))

            # ie if the number of different strings is the size of the cluster (= 1 for local)
            if sm == 1:
                Q[i, j] = 1

    row_sums = Q.sum(axis=1)
    Q = Q / row_sums[:, np.newaxis]

    return Q

find_proposal_matrix_uniform

find_proposal_matrix_uniform()

Function to find the proposal matrix for a given uniform chain.

Returns: Q (np.ndarray): The Q matrix for uniform proposal

Source code in src/qemcmc/spectralgap.py
def find_proposal_matrix_uniform(self):
    """
    Function to find the proposal matrix for a given uniform chain.

    Returns:
        Q (np.ndarray): The Q matrix for uniform proposal
    """

    Q = np.ones((2**self.model.n_spins, 2**self.model.n_spins)) / (self.proposal.n_spins**2 - 1)
    row_sums = Q.sum(axis=1)
    Q = Q / row_sums[:, np.newaxis]

    return Q

find_proposal_matrix_quantum

find_proposal_matrix_quantum()

Function to find the proposal matrix for a given QeMCMCChain object.

Returns: Q (np.ndarray): The Q matrix for quantum proposal

Source code in src/qemcmc/spectralgap.py
def find_proposal_matrix_quantum(self):
    """
    Function to find the proposal matrix for a given QeMCMCChain object.

    Returns:
        Q (np.ndarray): The Q matrix for quantum proposal
    """
    if not isinstance(self.proposal.gamma, (int, float)):
        raise ValueError("gamma must be a float to find the proposal matrix for quantum proposal, not a tuple. Got: ", self.proposal.gamma, "of type: ", type(self.proposal.gamma))
    if not isinstance(self.proposal.time, (int, float)):
        raise ValueError("time must be a number to find the proposal matrix for quantum proposal, not a tuple. Got: ", self.proposal.time, "of type: ", type(self.proposal.time))

    # Determine r to compute spectral gap
    if self.proposal.r is not None:
        r = self.proposal.r
    else:
        dt = self.proposal.delta_t if self.proposal.delta_t is not None else DEFAULT_DELTA_T
        r = max(1, int(np.floor(self.proposal.time / dt)))

    Q = np.zeros((2**self.model.n_spins, 2**self.model.n_spins))

    for i in range(2**self.model.n_spins):
        Q[i, :] += abs(self.proposal.CM.get_state_vector(self.model.S[i], self.proposal.coupling_weights, self.proposal.time, r, self.proposal.gamma)) ** 2
        # get_output_statevector(self.proposal.model.S[i])
    Q = Q

    return Q

find_proposal_matrix

find_proposal_matrix()

Function to find the proposal matrix for a given mcmc. This is not done by brute force

Source code in src/qemcmc/spectralgap.py
def find_proposal_matrix(self):
    """
    Function to find the proposal matrix for a given mcmc.
    This is not done by brute force
    """

    if self.proposal.method == "local":
        Q = self.find_proposal_matrix_local()
    elif self.proposal.method == "uniform":
        Q = self.find_proposal_matrix_uniform()
    elif self.proposal.method == "quantum":
        Q = self.find_proposal_matrix_quantum()
    else:
        raise ValueError("Method not recognised. Only 'local', 'uniform' or 'quantum' proposal methods are implimented in find_proposal_method.")

    return Q

find_spectral_gap

find_spectral_gap(A=None, Q=None)

Function to find the spectral gap of a given mcmc.

Parameters:

Name Type Description Default
A

The acceptance matrix for the mcmc (optional, if not given, will be calculated)

None
Q

The proposal matrix for the mcmc (optional, if not given, will be calculated)

None
Source code in src/qemcmc/spectralgap.py
def find_spectral_gap(self, A=None, Q=None):
    """
    Function to find the spectral gap of a given mcmc.

    Parameters
    ----------
    A (np.ndarray):
        The acceptance matrix for the mcmc (optional, if not given, will be calculated)
    Q (np.ndarray):
        The proposal matrix for the mcmc (optional, if not given, will be calculated)
    """

    if A is None:
        A = self.find_acceptance_matrix()
    if Q is None:
        Q = self.find_proposal_matrix()
        # Q = self.find_proposal_matrix_brute_force(multiple = 10*2**self.mcmc.n_spins)

    P = np.multiply(Q, A)

    # account for rejected swaps to add to s = s' matrix element
    for i in range(P.shape[0]):
        s = np.sum(P[i, :]) - P[i, i]
        P[i, i] = 1 - s

    # find eigenvalues
    e_vals, e_vecs = sp.linalg.eig(P)
    e_vals = np.flip(np.sort(abs(e_vals)))

    # find spectral gap
    delta = e_vals[1]
    delta = 1 - delta

    return delta