Source code for maoud.distributions

from abc import ABC, abstractmethod
import numpy as np
import math
import scipy.special as sps
import scipy.stats as stats
from .sampling import rejection_sampling


[docs]class Distribution(ABC): """An abstract class for a probability distribution. """
[docs] @abstractmethod def pdf(self, x): """Defines a 1D probability density function. Parameters ---------- x : 1darray Points where to evaluate the pdf. Returns ------- pdf : 1darray pdf values at `x`. """ pass
[docs] def rvs(self, x, size): """Returns a sample of length `size` in the range [x.min(), x.max()]. Parameters ---------- x : array The support of the pdf. size : int Size of the sample (number of realizations). """ return rejection_sampling(self.pdf, x, size)[0]
[docs]class ComplexDistribution(Distribution): """Defines a class for probability distribution arising from a complex random variable Z = X + jY, where j = sqrt(-1). """
[docs] def rvs(self, x, y, size): """Returns a sample of length `size` in the range [x.min(), x.max()] and [y.min(), y.max()] for the real and imaginary parts. Parameters ---------- x, y : arrays The support of the pdf of the real and imaginary parts, respectively. size : int Size of the sample (number of realizations). """ if len(x) != len(y): raise ValueError("x and y must have the same length.") return (rejection_sampling(self.pdf, x, size)[0] + 1j * rejection_sampling(self.pdf, y, size)[0])
[docs] def envelope_pdf(self, x): pass
[docs]class AlphaMu(Distribution): """Defines the α — μ probability distribution. For the theorectical aspects of the α — μ distribution, see M. D. Yacoub, The α — μ distribution: A physical fading model for the Stacy distribution, IEEE Trans. Veh. Technol., vol. 56, no. 1, pp. 27–34, 2007. Attributes ---------- alpha, mu : float, float Parameters that define the α — μ distribution. """ def __init__(self, alpha, mu): self.alpha = alpha self.mu = mu
[docs] def pdf(self, x): """Defines a univariate α — μ probability density function. Parameters ---------- x : 1darray Points where to evaluate the pdf. Returns ------- pdf : 1darray pdf values at `x`. """ return (self.alpha * self.mu ** self.mu * x ** (self.alpha * self.mu - 1.0) * np.exp(-self.mu * x ** self.alpha) / sps.gamma(self.mu))
[docs]class EtaMu(Distribution): """Defines the \eta — μ probability distribution. For the theorectical aspects of the \eta — μ distribution, see [ADD REFERENCE] Attributes ---------- eta, mu : float, float Parameters that define the eta — μ distribution. """ def __init__(self, eta, mu): self.eta = eta self.mu = mu
[docs] def pdf(self, x): return (4.0 * np.sqrt(np.pi) * self.mu ** (self.mu + 0.5) * x ** (2 * self.mu) * np.exp(-2. * self.mu * x * x / (1. + self.eta)) * sps.ive(self.mu - 0.5, 2. * self.eta * self.mu * x * x / (1. - self.eta * self.eta)) / (self.eta ** (self.mu - 0.5) * np.sqrt(1. - self.eta * self.eta) * sps.gamma(self.mu)))
[docs]class KappaMu(Distribution): """Defines the \kappa — μ probability distribution. For the theorectical aspects of the \kappa — μ distribution, see [ADD REFERENCE] Attributes ---------- kappa, mu : float, float Parameters that define the kappa — μ distribution. """ def __init__(self, kappa, mu): self.kappa = kappa self.mu = mu
[docs] def pdf(self, x): return (2. * self.mu * (1. + self.kappa) ** ((self.mu + 1.) * .5) * np.power(x, self.mu) * np.exp(-self.mu * (1. + self.kappa) * x * x - self.mu * self.kappa + 2. * x * self.mu * math.sqrt(self.kappa * (1. + self.kappa))) * sps.ive(self.mu - 1., 2. * self.mu * x * math.sqrt(self.kappa * (1. + self.kappa))) / self.kappa ** ((self.mu - 1.) * .5))
[docs]class ComplexAlphaMu(ComplexDistribution): def __init__(self, alpha, mu): self.alpha = alpha self.mu = mu
[docs] def pdf(self, x): return (self.mu ** (self.mu * 0.5) * np.abs(x) ** (self.mu - 1.0) * np.exp(-self.mu * x * x) / sps.gamma(self.mu * 0.5))
[docs] def envelope_pdf(self, x): return AlphaMu(self.alpha, self.mu).pdf(x)
[docs]class ComplexEtaMu(ComplexDistribution): def __init__(self, eta, mu): self.eta = eta self.mu = mu
[docs] def pdf(self, x): return ((2.0 * self.mu) ** self.mu * np.abs(x) ** (2.0 * self.mu - 1.0) * np.exp(-2.0 * self.mu * x * x / (1.0 - self.eta)) / (sps.gamma(self.mu) * np.power(1.0 - self.eta, self.mu)))
[docs]class ComplexKappaMu(object): def __init__(self, kappa, mu, phi): self.kappa = kappa self.mu = mu self.phi = phi
[docs] def real_part(self, x): p = np.sqrt(self.kappa / (1.0 + self.kappa)) * np.cos(self.phi) sigma2 = 1.0 / (2.0 * self.mu * (1.0 + self.kappa)) return (np.abs(x) ** (0.5 * self.mu) * np.exp(-(x - p) ** 2 / (2.0 * sigma2)) * sps.iv(self.mu * 0.5 - 1.0, np.abs(p * x) / sigma2) / (2.0 * sigma2 * np.abs(p) ** (0.5 * self.mu - 1.0) * np.cosh(p * x / sigma2)))
[docs] def imag_part(self, x): q = np.sqrt(self.kappa / (1.0 + self.kappa)) * np.sin(self.phi) sigma2 = 1.0 / (2.0 * self.mu * (1.0 + self.kappa)) return (np.abs(x) ** (0.5 * self.mu) * np.exp(-(x - q) ** 2 / (2.0 * sigma2)) * sps.iv(self.mu * 0.5 - 1.0, np.abs(q * x) / sigma2) / (2.0 * sigma2 * np.abs(q) ** (0.5 * self.mu - 1.0) * np.cosh(q * x / sigma2)))
[docs] def rvs(self, x, y, size): return (rejection_sampling(self.real_part, x, y, size)[0] + 1j * rejection_sampling(self.imag_part, x, y, size)[0])