Skip to content

Post Processing Utilities

oqd_heisenberg_ion.common.postprocess

ffs

free_fermion_energy

computes the ground state energy per site associated with a chain of free fermions

N should not be divisible by 4 for this calculation to yield the correct energy

Parameters:

  • N (int) –

    number of sites

  • phi (float) –

    boundary twist angle

Returns:

  • float

    energy per site

Source code in src/oqd_heisenberg_ion/common/postprocess/ffs.py
def free_fermion_energy(N, phi):
    """
    computes the ground state energy per site associated with a chain of free fermions

    N should not be divisible by 4 for this calculation to yield the correct energy

    Args:
        N (int): number of sites
        phi (float): boundary twist angle

    Returns:
        (float): energy per site
    """
    s_min = -int(N / 2)
    E = 0.0
    for i in range(N):
        s = s_min + i
        eval = -np.cos(2 * np.pi * s / N - phi / N)
        if eval < 0.0:
            E += eval

    # if (s != s_max):
    #    raise Exception("s not equal to s_max")

    return E / N

free_fermion_stiffness

computes the spin stiffness associated with a chain of free fermions

Parameters:

  • N (int) –

    number of sites

  • phi (float) –

    boundary twist angle

Returns:

  • float

    spin stiffness

Source code in src/oqd_heisenberg_ion/common/postprocess/ffs.py
def free_fermion_stiffness(N, phi):
    """
    computes the spin stiffness associated with a chain of free fermions

    Args:
        N (int): number of sites
        phi (float): boundary twist angle

    Returns:
        (float): spin stiffness
    """

    E_phi = free_fermion_energy(N, phi)
    E_zero = free_fermion_energy(N, 0.0)
    E_minus_phi = free_fermion_energy(N, -phi)

    rho = (N**2) * ((E_phi + E_minus_phi - 2.0 * E_zero) / (phi**2))

    return rho

swt

gamma_k

computes the coefficient gamma corresponding to the kth Fourier mode in linear spin wave theory for the power law decay spin-1/2 XY model For details, see: https://arxiv.org/pdf/2601.20058

Parameters:

  • N (int) –

    number of sites

  • k (int) –

    Fourier mode

  • theta (float) –

    twist angle

  • alpha (float) –

    power law decay exponent in interaction couplings

Returns:

  • float

    gamma_k

Source code in src/oqd_heisenberg_ion/common/postprocess/swt.py
def gamma_k(N, k, theta, alpha):
    """
    computes the coefficient gamma corresponding to the kth Fourier mode in linear spin wave theory for the power law decay spin-1/2 XY model
    For details, see: https://arxiv.org/pdf/2601.20058

    Args:
        N (int): number of sites
        k (int): Fourier mode
        theta (float): twist angle
        alpha (float): power law decay exponent in interaction couplings

    Returns:
        (float): gamma_k
    """

    gamma = 0.0
    num_terms = int((N - 1) / 2)
    for r in range(1, num_terms + 1):
        gamma += (1.0 / (r**alpha)) * np.cos(2.0 * np.pi * r * k / N) * np.cos(r * theta)

    return gamma

E_0_LSW

computes the ground state energy in linear spin wave theory for the power law decay spin-1/2 XY model. For details, see: https://arxiv.org/pdf/2601.20058

Parameters:

  • N (int) –

    number of sites

  • alpha (float) –

    power law decay exponent

  • J (float) –

    Energy scale

  • theta (float, default: 0.0 ) –

    Boundary twist angle. Defaults to 0.0.

Returns:

  • float

    ground state energy in linear spin wave theory

Source code in src/oqd_heisenberg_ion/common/postprocess/swt.py
def E_0_LSW(N, alpha, J, theta=0.0):
    """
    computes the ground state energy in linear spin wave theory for the power law decay spin-1/2 XY model.
    For details, see: https://arxiv.org/pdf/2601.20058

    Args:
        N (int): number of sites
        alpha (float): power law decay exponent
        J (float): Energy scale
        theta (float, optional): Boundary twist angle. Defaults to 0.0.

    Returns:
        (float): ground state energy in linear spin wave theory
    """

    gamma_0 = gamma_k(N, 0, theta, alpha)
    energy = (-3.0 / 2.0) * N

    for j in range(N):
        gamma_j = gamma_k(N, j, theta, alpha)
        energy += np.sqrt(1.0 - gamma_j / gamma_0) + gamma_j / (2.0 * gamma_0)

    energy *= J * gamma_0 / 2.0

    return energy

E_0_MF

computes the ground state energy in mean field theory for the power law decay spin-1/2 XY model. For details, see: https://arxiv.org/pdf/2601.20058

Parameters:

  • N (int) –

    number of sites

  • alpha (float) –

    power law decay exponent

  • J (float) –

    Energy scale

  • theta (float, default: 0.0 ) –

    Boundary twist angle. Defaults to 0.0.

Returns:

  • float

    ground state energy in mean field theory

Source code in src/oqd_heisenberg_ion/common/postprocess/swt.py
def E_0_MF(N, alpha, J, theta=0.0):
    """
    computes the ground state energy in mean field theory for the power law decay spin-1/2 XY model.
    For details, see: https://arxiv.org/pdf/2601.20058

    Args:
        N (int): number of sites
        alpha (float): power law decay exponent
        J (float): Energy scale
        theta (float, optional): Boundary twist angle. Defaults to 0.0.

    Returns:
        (float): ground state energy in mean field theory
    """

    gamma_0 = gamma_k(N, 0, theta, alpha)

    energy = -J * N * gamma_0 / 4.0

    return energy

E_0_LSW_NN

computes the ground state energy in linear spin wave theory for the nearest neighbor spin-1/2 XY model. For details, see: https://arxiv.org/pdf/2601.20058

Parameters:

  • N (int) –

    number of sites

  • J (float) –

    Energy scale

Returns:

  • float

    ground state energy in linear spin wave theory

Source code in src/oqd_heisenberg_ion/common/postprocess/swt.py
def E_0_LSW_NN(N, J):
    """
    computes the ground state energy in linear spin wave theory for the nearest neighbor spin-1/2 XY model.
    For details, see: https://arxiv.org/pdf/2601.20058

    Args:
        N (int): number of sites
        J (float): Energy scale

    Returns:
        (float): ground state energy in linear spin wave theory
    """

    energy = (-3.0 / 2.0) * N

    for j in range(N):
        gamma_j = np.cos(2.0 * np.pi * j / N)
        energy += np.sqrt(1.0 - gamma_j) + gamma_j / (2.0)

    energy *= J / 2.0

    return energy

rho_2

computes the spin stiffness for the power law decay spin-1/2 XY model in linear spin wave theory For details, see: https://arxiv.org/pdf/2601.20058

Parameters:

  • N (int) –

    number of sites

  • alpha (float) –

    power law decay exponent

  • theta (float) –

    twist angle

  • J_1 (float) –

    energy scale

  • J_2 (float, default: 1.0 ) –

    energy scale. Defaults to 1.0. Should typically be set equal to J_1

Returns:

  • float

    spin wave theory spin stiffness

Source code in src/oqd_heisenberg_ion/common/postprocess/swt.py
def rho_2(N, alpha, theta, J_1, J_2=1.0):
    """
    computes the spin stiffness for the power law decay spin-1/2 XY model in linear spin wave theory
    For details, see: https://arxiv.org/pdf/2601.20058

    Args:
        N (int): number of sites
        alpha (float): power law decay exponent
        theta (float): twist angle
        J_1 (float): energy scale
        J_2 (float, optional): energy scale. Defaults to 1.0. Should typically be set equal to J_1

    Returns:
        (float): spin wave theory spin stiffness
    """

    energy_theta = E_0_LSW(N, alpha, J_1, theta)
    energy_0 = E_0_LSW(N, alpha, J_2, 0.0)
    rho_N_alpha = 2.0 * (energy_theta - energy_0) / (N * (theta**2))

    return rho_N_alpha

rho_mf

computes the spin stiffness for the power law decay spin-1/2 XY model in mean field theory For details, see: https://arxiv.org/pdf/2601.20058

Parameters:

  • N (int) –

    number of sites

  • alpha (float) –

    power law decay exponent

  • theta (float) –

    twist angle

  • J (float) –

    energy scale

Returns:

  • float

    mean field theory spin stiffness

Source code in src/oqd_heisenberg_ion/common/postprocess/swt.py
def rho_mf(N, alpha, theta, J):
    """
    computes the spin stiffness for the power law decay spin-1/2 XY model in mean field theory
    For details, see: https://arxiv.org/pdf/2601.20058

    Args:
        N (int): number of sites
        alpha (float): power law decay exponent
        theta (float): twist angle
        J (float): energy scale

    Returns:
        (float): mean field theory spin stiffness
    """

    energy_theta = E_0_MF(N, alpha, J, theta)
    energy_0 = E_0_MF(N, alpha, J, 0.0)
    rho_N_alpha = 2.0 * (energy_theta - energy_0) / (N * (theta**2))

    return rho_N_alpha

utils

combine_different_runs

concatenates two data sets

Parameters:

  • data_1 (ndarray[float]) –

    T1 x 1 array containing the first data set

  • data_2 (ndarray[float]) –

    T2 x 1 array containing the second data set

  • drop_samples_1 (int) –

    number of samples to drop from the top in data1

  • drop_samples_2 (int) –

    number of samples to drop from the top in data2

Returns:

  • ndarray[float]

    (T3 x 1) array containing the concatenated data set

Source code in src/oqd_heisenberg_ion/common/postprocess/utils.py
def combine_different_runs(data_1, data_2, drop_samples_1, drop_samples_2):
    """
    concatenates two data sets

    Args:
        data_1 (numpy.ndarray[float]): T1 x 1 array containing the first data set
        data_2 (numpy.ndarray[float]): T2 x 1 array containing the second data set
        drop_samples_1 (int): number of samples to drop from the top in data1
        drop_samples_2 (int): number of samples to drop from the top in data2

    Returns:
        (numpy.ndarray[float]): (T3 x 1) array containing the concatenated data set
    """

    data_3 = []
    data_1 = data_1[drop_samples_1:]
    data_2 = data_2[drop_samples_2:]

    for i in range(len(data_1)):
        data_3.append(data_1[i])

    for i in range(len(data_2)):
        data_3.append(data_2[i])

    return data_3

statistics_binning

Uses binning to post process QMC estimator data

Parameters:

  • arr (ndarray[float]) –

    T x 1 array of QMC data where T is the number of simulation steps

  • auto_corr_drop (int) –

    number of points to remove for auto-correlation

  • eq_drop (int) –

    number of points to remove for equilibration

Returns:

  • tuple[float, float]

    mean and standard error of QMC data

Source code in src/oqd_heisenberg_ion/common/postprocess/utils.py
def statistics_binning(arr, auto_corr_drop, eq_drop):
    """
    Uses binning to post process QMC estimator data

    Args:
        arr (numpy.ndarray[float]): T x 1 array of QMC data where T is the number of simulation steps
        auto_corr_drop (int): number of points to remove for auto-correlation
        eq_drop (int): number of points to remove for equilibration

    Returns:
        (tuple[float,float]): mean and standard error of QMC data
    """

    arr2 = arr[eq_drop:]
    arr3 = arr2[0::auto_corr_drop]
    workingNdim = int(math.log(len(arr3)) / math.log(2))
    mean = np.mean(arr3)
    standardError = max_error_binning(arr3, workingNdim - 6)
    return mean, standardError

error_propagation

computes statistical standard error of array

Parameters:

  • data (ndarray[float]) –

    T x 1 array containing the data

Returns:

  • float

    standard error

Source code in src/oqd_heisenberg_ion/common/postprocess/utils.py
def error_propagation(data):
    """
    computes statistical standard error of array

    Args:
        data (numpy.ndarray[float]): T x 1 array containing the data

    Returns:
        (float): standard error
    """

    ndim = len(data)
    error = np.std(data, ddof=0) / np.sqrt(ndim)

    return error

max_error_binning

helper function containing the binning error logic

Parameters:

  • data (ndarray[float]) –

    T x 1 array containing the data

  • dim (int) –

    number of binning levels

Raises:

  • Exception

    if dim is less than 1 because of insufficient data

Returns:

  • float

    binning error

Source code in src/oqd_heisenberg_ion/common/postprocess/utils.py
def max_error_binning(data, dim):
    """
    helper function containing the binning error logic

    Args:
        data (numpy.ndarray[float]): T x 1 array containing the data
        dim (int): number of binning levels

    Raises:
        Exception: if dim is less than 1 because of insufficient data

    Returns:
        (float): binning error
    """

    if dim <= 1:
        raise Exception("Not enough points MC steps were used for the binning method\n")

    error = np.zeros(dim)
    error[0] = error_propagation(data)

    for i in range(1, dim):
        bin_dim = int(len(data) / 2)
        data_bin = np.zeros(bin_dim)

        for j in range(bin_dim):
            data_bin[j] = 0.5 * (data[2 * j] + data[2 * j + 1])
        data = data_bin
        error[i] = error_propagation(data)

    return np.max(error)

ed_energy

computes the equilibrium energy from exact diagonalization calculations at given temperature

Parameters:

  • evals (ndarray[float]) –

    T x 1 array containing the energies for each state

  • T (float) –

    temperature

Returns:

  • float

    equilibrium energy

Source code in src/oqd_heisenberg_ion/common/postprocess/utils.py
def ed_energy(evals, T):
    """
    computes the equilibrium energy from exact diagonalization calculations at given temperature

    Args:
        evals (numpy.ndarray[float]): T x 1 array containing the energies for each state
        T (float): temperature

    Returns:
        (float): equilibrium energy
    """

    beta = 1.0 / T

    energy = 0.0
    partition = 0.0

    for i in range(len(evals)):
        energy += np.exp(-beta * evals[i]) * evals[i]
        partition += np.exp(-beta * evals[i])

    energy /= partition

    return energy

compute_histogram_from_shot_data

computes the histogram associated with shot data

Parameters:

  • N (int) –

    number of sites in the lattice

  • num_shots (int) –

    number of data points

  • shot_data (ndarray[int]) –

    T x N array of shot data. shot_data[t,k] represents the spin configuration at time t and site k

Returns:

  • ndarray[float]

    (T x 1) array containing the histogram frequences where T is the number of possible bitstrings given N

Source code in src/oqd_heisenberg_ion/common/postprocess/utils.py
def compute_histogram_from_shot_data(N, num_shots, shot_data):
    """
    computes the histogram associated with shot data

    Args:
        N (int): number of sites in the lattice
        num_shots (int): number of data points
        shot_data (numpy.ndarray[int]): T x N array of shot data. shot_data[t,k] represents the spin configuration at time t and site k

    Returns:
        (numpy.ndarray[float]): (T x 1) array containing the histogram frequences where T is the number of possible bitstrings given N
    """

    num_bits = 2**N
    freqs = np.zeros(num_bits)
    for i in range(num_shots):
        bit_str = 0
        for j in range(N):
            signed_spin_config = shot_data[i, j]
            bit_i_j = int(0.5 * (signed_spin_config + np.abs(signed_spin_config)))
            bit_str += (2 ** (j)) * (bit_i_j)
        freqs[bit_str] += 1.0 / num_shots

    return freqs

kl_divergence

computes the KL divergence between two histograms

Parameters:

  • hist1 (ndarray[float]) –

    T x 1 array containing the histogram frequences of the first distribution

  • hist2 (ndarray[float]) –

    T x 1 array containing the histogram frequences of the second distribution

  • num_bits (int) –

    Number of unique measurements in histograms

Returns:

  • float

    KL divergence between data sets

Source code in src/oqd_heisenberg_ion/common/postprocess/utils.py
def kl_divergence(hist1, hist2, num_bits):
    """
    computes the KL divergence between two histograms

    Args:
        hist1 (numpy.ndarray[float]): T x 1 array containing the histogram frequences of the first distribution
        hist2 (numpy.ndarray[float]): T x 1 array containing the histogram frequences of the second distribution
        num_bits (int): Number of unique measurements in histograms

    Returns:
        (float): KL divergence between data sets
    """

    kl_divergence = 0.0
    for j in range(num_bits):
        if hist1[j] != 0 and hist2[j] != 0:
            kl_divergence += hist1[j] * (np.log(hist1[j]) - np.log(hist2[j]))

    return kl_divergence