Source code for iq_readout.pdfs

"""Module containing the probability density functions used in the classifiers.

The probability density functions have been implemented keeping in mind that
they will be used for fitting using ``scipy.optimize.curve_fit``, thus all
its arguments are split into scalars while the input points have been
grouped into a single multidimensional variable.
"""
import numpy as np
from scipy.special import erf

from .utils import check_2d_input, rotate_data, get_angle


[docs] def simple_1d_gaussian( x: np.ndarray, mu: float, sigma: float, ) -> np.ndarray: """Probability density function of a 1D Gaussian. It has mean ``mu`` and standard deviation ``sigma``. Parameters ---------- x : np.ndarray(...) Points in the 1D space. mu Mean of the first coordinate. sigma Standard deviation of the two coordinates. Returns ------- prob : np.ndarray(...) Values of the probability density function. """ prob = 1 / np.sqrt(2 * np.pi * sigma**2) * np.exp(-0.5 * (x - mu) ** 2 / sigma**2) return prob
[docs] def simple_1d_gaussian_double_mixture( x: np.ndarray, mu_0: float, mu_1: float, sigma: float, angle: float, ) -> np.ndarray: """ Probability density function corresponding to the sum of two 1D Gaussians with the same standard deviation. It has mean ``mu_0`` and ``mu_1``, and standard deviation ``sigma``. Parameters ---------- x : np.ndarray(...) Points in the 1D space. mu_0 Mean for the first Gaussian. mu_1 Mean for the second Gaussian. sigma Standard deviation of the two coordinates for both Gaussians. angle Weight of the first Gaussian is sin(angle)**2 and of the second Gaussian is cos(angle)**2 to ensure that the probability density function is normalized. Returns ------- prob : np.ndarray(...) Values of the probability density function. """ a_0, a_1 = np.sin(angle) ** 2, np.cos(angle) ** 2 prob_0 = simple_1d_gaussian(x, mu=mu_0, sigma=sigma) prob_1 = simple_1d_gaussian(x, mu=mu_1, sigma=sigma) prob = a_0 * prob_0 + a_1 * prob_1 return prob
[docs] def simple_2d_gaussian( z: np.ndarray, mu_x: float, mu_y: float, sigma: float, ) -> np.ndarray: """ Probability density function of a 2D Gaussian. It has mean ``(mu_x, mu_y)`` and covariance matrix ``diag(sigma**2, sigma**2)``. Parameters ---------- z : np.ndarray(..., 2) Points in the 2D space. mu_x Mean of the first coordinate. mu_y Mean of the second coordinate. sigma Standard deviation of the two coordinates. Returns ------- prob : np.ndarray(...) Values of the probability density function. """ check_2d_input(z) x, y = z[..., 0], z[..., 1] x_norm, y_norm = (x - mu_x) / sigma, (y - mu_y) / sigma prob = 1 / (2 * np.pi * sigma**2) * np.exp(-0.5 * (x_norm**2 + y_norm**2)) return prob
[docs] def simple_2d_gaussian_double_mixture( z: np.ndarray, mu_0_x: float, mu_0_y: float, mu_1_x: float, mu_1_y: float, sigma: float, angle: float, ) -> np.ndarray: """ Probability density function corresponding to the sum of two 2D Gaussians. The two Gaussians have means ``(mu_0_x, mu_0_y)`` and ``(mu_1_x, mu_1_y)``, and same covariance matrix ``diag(sigma**2, sigma**2)``. The weight of the first Gaussian is ``sin(angle)**2`` and of the second Gaussian is ``cos(angle)**2`` to ensure that the PDF is normalized. Parameters ---------- z : np.ndarray(..., 2) Points in the 2D space. mu_0_x Mean of the first coordinate for the first Gaussian. mu_0_y Mean of the second coordinate for the firs Gaussian. mu_1_x Mean of the first coordinate for the second Gaussian. mu_1_y Mean of the second coordinate for the second Gaussian. sigma Standard deviation of the two coordinates for both Gaussians. angle Weight factor for the Gaussian mixture. Returns ------- prob : np.ndarray(...) Values of the probability density function. """ check_2d_input(z) a_0, a_1 = np.sin(angle) ** 2, np.cos(angle) ** 2 prob_0 = simple_2d_gaussian(z, mu_x=mu_0_x, mu_y=mu_0_y, sigma=sigma) prob_1 = simple_2d_gaussian(z, mu_x=mu_1_x, mu_y=mu_1_y, sigma=sigma) prob = a_0 * prob_0 + a_1 * prob_1 return prob
[docs] def simple_2d_gaussian_triple_mixture( z: np.ndarray, mu_0_x: float, mu_0_y: float, mu_1_x: float, mu_1_y: float, mu_2_x: float, mu_2_y: float, sigma: float, angle1: float, angle2: float, ) -> np.ndarray: """ Probability density function corresponding to the sum of three 2D Gaussians. The three Gaussians have means ``(mu_0_x, mu_0_y)``, ``(mu_1_x, mu_1_y)``, and ``(mu_2_x, mu_2_y)``, and same covariance matrix ``diag(sigma**2, sigma**2)``. The weight of the fist Gaussian is ``sin(angle2)**2 * cos(angle1)**2`` and of the second Gaussian is ``sin(angle2)**2 * sin(angle1)**2`` and of the thrid Gaussian is ``cos(angle2)**2`` to ensure that the probability density function is normalized. Parameters ---------- z : np.ndarray(..., 2) Points in the 2D space mu_0_x Mean of the first coordinate for the first Gaussian. mu_0_y Mean of the second coordinate for the firs Gaussian. mu_1_x Mean of the first coordinate for the second Gaussian. mu_1_y Mean of the second coordinate for the second Gaussian. mu_2_x Mean of the first coordinate for the thrid Gaussian. mu_2_y Mean of the second coordinate for the thid Gaussian. sigma Standard deviation of the two coordinates for the Gaussians. angle1 Weight factor for the Gaussian mixture. angle2 Weight factor for the Gaussian mixture. """ check_2d_input(z) a_0, a_1, a_2 = ( np.sin(angle1) ** 2 * np.cos(angle2) ** 2, np.sin(angle1) ** 2 * np.sin(angle2) ** 2, np.cos(angle1) ** 2, ) prob_0 = simple_2d_gaussian(z, mu_x=mu_0_x, mu_y=mu_0_y, sigma=sigma) prob_1 = simple_2d_gaussian(z, mu_x=mu_1_x, mu_y=mu_1_y, sigma=sigma) prob_2 = simple_2d_gaussian(z, mu_x=mu_2_x, mu_y=mu_2_y, sigma=sigma) prob = a_0 * prob_0 + a_1 * prob_1 + a_2 * prob_2 return prob
[docs] def decay_amplitude_1d_pdf( x: np.ndarray, mu_0: float, mu_1: float, sigma: float, angle: float, t1_norm: float, ): """ Probability density function observed when considering amplitude decay during the measurement. Parameters ---------- x : np.ndarray(...) Points in the 1D space. mu_0 Mean for the Gaussian noise for state ``0``. mu_1 Mean for the Gaussian noise for state ``1``. sigma Standard deviation of the Gaussian noise for both states. t1_norm T1 normalized with respect to the measurement duration. Returns ------- prob : np.ndarray(...) Values of the probability density function. Notes ----- See ``decaylda.md``. """ a_0, a_1 = np.sin(angle) ** 2, np.cos(angle) ** 2 C = np.sign(mu_1 - mu_0) * (mu_0 - x) / (np.sqrt(2) * sigma) + sigma / ( np.sqrt(2) * np.abs(mu_1 - mu_0) * t1_norm ) P = 0.5 * (mu_1 - mu_0) ** 2 / sigma**2 prob_0 = np.exp(-0.5 * (x - mu_1) ** 2 / sigma**2) / np.sqrt(2 * np.pi * sigma**2) prob_1 = ( np.exp(-0.5 * (x - mu_0) ** 2 / sigma**2 + C**2) / (np.sqrt(2 * np.pi * sigma**2) * t1_norm) * np.sqrt(np.pi / (4 * P)) * (erf(C + np.sqrt(P)) - erf(C)) / (1 - np.exp(-1 / t1_norm)) ) prob = a_0 * prob_0 + a_1 * prob_1 return prob
[docs] def decay_amplitude_2d_pdf( z: np.ndarray, mu_0_x: float, mu_0_y: float, mu_1_x: float, mu_1_y: float, sigma: float, angle: float, t1_norm: float, ) -> np.ndarray: """ Probability density function corresponding to a `decay_amplitude_1d_pdf` in the (parallel) projected axis along mu_0 and mu_1 and a `simple_1d_gaussian` in the perpendicular dimension Parameters ---------- z : np.ndarray(..., 2) Points in the 2D space mu_i_x Mean of the first coordinate for the i^th Gaussian mu_i_y Mean of the second coordinate for the i^th Gaussian sigma Standard deviation of the two coordinates for both Gaussians angle Weight of the 1st Gaussian is sin(angle)**2 and of the 2nd Gaussian is cos(angle)**2 to ensure that the PDF is normalized t1_norm T1 normalized with respect to the measurement duration Returns ------- prob : np.ndarray(...) Values of the probability density function """ check_2d_input(z) mu_0, mu_1 = np.array([mu_0_x, mu_0_y]), np.array([mu_1_x, mu_1_y]) rot_angle = get_angle(mu_1 - mu_0) z_rot = rotate_data(z, -rot_angle) mu_0_rot, mu_1_rot = rotate_data(mu_0, -rot_angle), rotate_data(mu_1, -rot_angle) prob_para = decay_amplitude_1d_pdf( z_rot[..., 0], mu_0=mu_0_rot[0], mu_1=mu_1_rot[0], sigma=sigma, angle=angle, t1_norm=t1_norm, ) prob_perp = simple_1d_gaussian( z_rot[..., 1], mu=mu_0_rot[1], # it is the same as mu_1_rot[1] sigma=sigma, ) prob = prob_para * prob_perp return prob
[docs] def pdf_from_hist1d(x, bins, pdf_values): """ Returns the PDF value from the 1D histogram bins closest to `x`. Parameters ---------- x : np.ndarray(...) Points in the 1D space. bins: np.ndarray(n_bins) Centers of the bins of the 1D histogram. pdf_values: np.ndarray(n_bins) Normalized counts of the 1D histogram. Returns ------- prob: np.ndarray(...) Values of the probability density function. """ bin_sep = bins[1] - bins[0] idxs = np.searchsorted(bins, x + bin_sep / 2, side="right") - 1 prob = pdf_values[idxs] prob[(x < bins[0] - bin_sep / 2) | (x > bins[-1] - bin_sep / 2)] = 0 return prob
[docs] def pdf_from_hist2d(z, bins_x, bins_y, pdf_values): """ Returns the PDF value from the 2D histogram bins closest to `x`. Parameters ---------- z : np.ndarray(..., 2) Points in the 2D space. bins_x: np.ndarray(n_bins_x) Centers of the X-axis bins of the 2D histogram. bins_y: np.ndarray(n_bins_y) Centers of the Y-axis bins of the 2D histogram. pdf_values: np.ndarray(n_bins_x, n_bins_y) Normalized counts of the 2D histogram. Returns ------- prob: np.ndarray(...) Values of the probability density function. """ check_2d_input(z) bin_sep_x = bins_x[1] - bins_x[0] bin_sep_y = bins_y[1] - bins_y[0] idxs_x = ( np.searchsorted(bins_x, z[..., 0] + bin_sep_x / 2, side="right") - 1 ) idxs_y = ( np.searchsorted(bins_y, z[..., 1] + bin_sep_y / 2, side="right") - 1 ) prob = pdf_values[idxs_x.flatten(), idxs_y.flatten()] prob = prob.reshape(np.shape(z)[:-1]) prob[(z[..., 0] < bins_x[0] - bin_sep_x / 2) | (z[..., 0] > bins_x[-1] - bin_sep_x / 2)] = 0 prob[(z[..., 1] < bins_y[0] - bin_sep_y / 2) | (z[..., 1] > bins_y[-1] - bin_sep_y / 2)] = 0 return prob