def phyper_poisson(lam, beta, N, norm=True): """ Bardwell, G. E., & Crow, E. L. (1964). A two-parameter family of hyper-Poisson distributions. Journal of the American Statistical Association, 59(305), 133-141. Formula (6) On the Hyper-Poisson Distribution and its Generalization with Applications Bayo H. Lawal Formulas (2.1, 2.2) Parameters ---------- lam : float Parameter 1. beta : float Parameter 2. N : int maximal photon number. Returns ------- The photon-number distribution """ def phi_function(beta, lam, N=100): k = np.arange(N) return np.sum(Γ(beta) / Γ(beta + k) * lam**k) n = np.arange(N) phi = phi_function(beta, lam) P = Γ(beta) / Γ(beta + n) * lam**n / phi return normalize(P) if norm else P
def qthermal_unpolarized(mean, dof, N, norm=True): """ Дж. Гудмен, Статистическая оптика, ф-ла 9.2.29 при P = 0 Parameters ---------- mean : float Mean value of the distribution. dof : int Ration of measurement time to coherence time. N : int Maximal photocounts number. Returns ------- np.ndarray Photounts distribution. """ @np.vectorize def fsum(_m): k = np.arange(_m + 1) return np.sum( Γ(_m - k + dof) / (Γ(_m - k + 1) * Γ(dof)) * Γ(k + dof) / (Γ(k + 1) * Γ(dof))) m = np.arange(N) P = fsum(m) * (1 + 2 * dof / mean) ** (- m) * \ (1 + mean / 2 / dof) ** (- 2 * dof) return normalize(P) if norm else P
def psqueezed_coherent1(ampl, sq_coeff, N, norm=True): vac = basis(N, 0) d = displace(N, ampl) s = squeeze(N, sq_coeff) print('Squeeze', np.exp(-2 * np.abs(sq_coeff)) / 4) P = d * s * vac return normalize(P) if norm else P
def mrec_maxent_pn(Q, qe: float, nmax: int = 0, max_order: int = 2): mmax = len(Q) if nmax == 0: nmax = mmax moments = imoms(Q, max_order, qe) P, _ = reconstruct(moments.astype(np.float64), rndvar=np.arange(nmax)) return normalize(P)
def compensate(Q: np.ndarray, p_crosstalk: float) -> np.ndarray: """ Remove crosstalk noise from photocounting statistics. We use model with 4 neighbors with saturation. See formula 2.21 in [1] Parameters ---------- Q : iterable The photocounting statistics. p_crosstalk : float The probability of a single crosstalk event. Returns ------- Qcorr : ndarray Denoised photocounting statistics. References ---------- .. [1] Gallego, L., et al. "Modeling crosstalk in silicon photomultipliers." Journal of instrumentation 8.05 (2013): P05010. https://iopscience.iop.org/article/10.1088/1748-0221/8/05/P05010/pdf """ N = len(Q) eps = total_pcrosstalk(p_crosstalk) Qcorr = np.zeros(N) Qcorr[0] = Q[0] Qcorr[1] = Q[1] / (1 - eps) for m in range(2, N): k = np.arange(1, m) c1 = (1 - eps)**-m c2 = np.sum( Qcorr[k] * np.vectorize(p_crosstalk_m, otypes=[float])(k, m, p_crosstalk)) Qcorr[m] = c1 * (Q[m] - c2) return normalize(Qcorr)
def psqueezed_vacuum(r, theta, N, norm=True): """ 2-mode squeezed vacuum state Parameters --------- r : complex Pump parameter [0, 1] theta : float relative phase shift of two modes one by one N : int maximal photon number. Returns ------- 2-mode squeezed vacuum photon-number distribution """ n = np.arange(N) distribution = np.tanh(r)**n / np.cosh(r) * (1 - n % 2) P = distribution**2 return normalize(P) if norm else P
def qthermal_polarized(mean, dof, N, norm=True): """ Дж. Гудмен, Статистическая оптика, ф-ла 9.2.24 Parameters ---------- mean : float Mean value of the distribution. dof : int Ration of measurement time to coherence time. N : int Maximal photocounts number. Returns ------- np.ndarray Photounts distribution. """ m = np.arange(N) P = Γ(m + dof) / (Γ(m + 1) * Γ(dof)) * (1 + dof / mean)**(-m) * ( 1 + mean / dof)**(-dof) return normalize(P) if norm else P
def pcompound_poisson(mu: float, a: float, N: int, norm=True): """ Bogdanov, Y. I., Bogdanova, N. A., Katamadze, K. G., Avosopyants, G. V., & Lukichev, V. F. (2016). Study of photon statistics using a compound Poisson distribution and quadrature measurements. Optoelectronics, Instrumentation and Data Processing, 52(5), 475-485. Formula (10) Parameters ---------- mu : float mean value. a : float a parameter. N : int maximal photon number. norm : bool, optional Flag to normalization. The default is True. Returns ------- The photon-number distribution """ n = np.arange(N) if a > 0: P = (mu / a) ** n * Γ(a + n) / Γ(a) / \ Γ(n + 1) / (1 + mu / a) ** (n + a) elif a < 0: if int(a) == a and mu == -a: P = pfock(-a, N) else: P = (mu / a) ** n / (beta(a - 1, n + 1) * (a - 1)) / \ (1 + mu / a) ** (n + a) return normalize(P) if norm else P
def pthermal_photonadd(mean, photonadd, N, norm=True): """ Barnett, Stephen M., et al. "Statistics of photon-subtracted and photon-added states." Physical Review A 98.1 (2018): 013809. Parameters ---------- mean : float mean of distribution. photonadd : int count of added photons. N : int maximal photon number. Returns ------- The photon-number distribution """ n = np.arange(N) P = mean**(n - photonadd) / (1 + mean)**(n + 1) * binom(n, photonadd) P[:photonadd] = 0 return normalize(P) if norm else P
def pfock(mean, N, norm=True): if np.floor(mean) != mean: raise ValueError(f'Fock state energy must be int, not {mean}') P = np.zeros(N) P[mean] = 1 return normalize(P) if norm else P
def pthermal(mean, N, norm=True): P = pthermal_polarized(mean, 1, N) return normalize(P) if norm else P
def ppoisson(mean, N, norm=True): P = poisson.pmf(np.arange(N), mean) return normalize(P) if norm else P
def pthermal_polarized(mean, dof, N, norm=True): p = 1 - mean / (dof + mean) P = nbinom.pmf(np.arange(N), dof, p) return normalize(P) if norm else P
def hist2Q(hist: np.ndarray, bins: np.ndarray, discrete: int, method: str = 'sum', threshold: float = 1, peak_width: float = 1, down_width: float = 1, manual_zero_offset: int = 0, plot: bool = False, logplot: bool = False, *args, **kwargs) -> np.ndarray: """ Build photocounting statistics from an experimental histogram by gaussian-hermite polynoms or simple sum Parameters ---------- hist : ndarray Experimental histogram values. bins : ndarray Experimental histogram bins. discrete : int The amplitude of single photocount pulse in points. manual_zero_offset: int, optional Offset for calculate zero-photon probability in presence of non-zero noise pulses (in points). The default is 0. threshold : float, optional Minimal number of events to find histogram peak. The default is 1. peak_width : float, optional The width of peaks. It must be greater than 1 if the histogram is made by oscilloscope or 'max' method. The default is 1. down_width : float, optional The width of downs. It must be greater than 1 if the histogram is made by oscilloscope or 'max' method. The default is 1. plot : bool, optional Flag to plot hist and results of find_peaks. The default is False. logplot : bool Enable log yscale for histogram plotting. The default is False. method : {'sum', 'fit', 'manual'} Method of the photocounting statistics construction. 'sum' is a simple summation between minimums of the histogram 'fit' is a gauss-hermite function fitteing like in [1] 'manual' is a simple summation of intervals with fixed length. Returns ------- Q : ndarray The photocounting statistics. References ---------- .. [1] Ramilli, Marco, et al. "Photon-number statistics with silicon photomultipliers." JOSA B 27.5 (2010): 852-862. """ if method != 'manual': discrete = int(discrete * 0.9) downs, _ = find_peaks(-hist, distance=discrete, width=down_width) downs = np.append([0], downs) peaks, _ = find_peaks(np.concatenate(([0], hist)), threshold=threshold, distance=discrete, width=peak_width, plateau_size=(0, 10)) peaks -= 1 if peaks == []: raise ValueError( 'Histogram peaks were not found with given settings') if plot: plt.scatter(bins[peaks], hist[peaks]) plt.scatter(bins[downs], hist[downs]) if method == 'manual': Q = [] peaks = np.arange(manual_zero_offset, len(bins), discrete) for p in peaks: if p == manual_zero_offset: low = 0 else: low = int(max(0, p - discrete // 2)) top = int(min(p + discrete // 2, len(hist) - 1)) Q.append(np.sum(hist[low:top])) if plot: plt.axvline(bins[low], linestyle=':', color='black') plt.axvline(bins[top], linestyle=':', color='black') if top != len(hist) - 1: Q.append(sum(hist[top:])) elif method == 'sum': Q = construct_q_sum(hist, peaks, downs) elif method == 'fit': Q = construct_q_fit(hist, bins, peaks, downs) if plot: plt.plot(bins, hist) plt.xlabel('Amplitude, V') plt.ylabel("Events' number") if logplot: plt.yscale('log') plt.show() return normalize(Q)