Source code for comyx.fading.rician
from __future__ import annotations
from typing import Any, Tuple, Union
import numpy as np
import numpy.typing as npt
import scipy.stats as stats
from scipy.special import i0
from ..utils import laguerre
NDArrayFloat = npt.NDArray[np.floating[Any]]
[docs]
class Rician:
r"""Represents the :math:`\text{Rician}(K, \sigma)` distribution.
The Rice distribution or Rician distribution (or, less commonly, Ricean
distribution) is the probability distribution of the magnitude of a
circularly-symmetric bivariate normal random variable, possibly with
non-zero mean (noncentral).
Density Function
.. math::
f(x; \nu, \sigma) = \frac{x}{\sigma^2} \exp\left(-\frac{x^2 + \nu^2}{2\sigma^2}\right) I_0\left(\frac{x\nu}{\sigma^2}\right)
, where :math:`I_0` is the modified Bessel function of the first kind.
Expected value
.. math::
\sigma \sqrt{\frac{\pi}{2}} \exp\left(-\frac{\nu^2}{2\sigma^2}\right)
Variance
.. math::
2\sigma^2 + \nu^2 - \frac{\pi\sigma^2}{2}
RMS value
.. math::
\sigma \sqrt{2 + \frac{\pi}{2}}
Reference:
https://en.wikipedia.org/wiki/Rice_distribution
"""
def __init__(self, K: float, sigma: float = 1) -> None:
"""Initialize the Rician distribution with the given parameters.
Args:
K: Rician factor, i.e., ratio between the power of direct path and
the power of scattered paths.
sigma: The scale parameter, which is the standard deviation of the
distribution.
"""
self.K = K
self.sigma = sigma
self.omega = (2 * self.K + 2) * self.sigma**2
self.nu = np.sqrt((K / (1 + K)) * self.omega)
[docs]
def pdf(self, x: NDArrayFloat) -> NDArrayFloat:
"""Probability density function of the Rician distribution.
Args:
x: Value at which pdf is evaluated.
Returns:
Value of the probability density function evaluated at x.
"""
return (
(x / self.sigma**2)
* np.exp(-(x**2 + self.nu**2) / (2 * self.sigma**2))
* i0(x * self.nu / self.sigma**2)
)
[docs]
def cdf(self, x: NDArrayFloat) -> NDArrayFloat:
"""Cumulative distribution function of the the Rician distribution.
Args:
x: Value at which cdf is evaluated.
Returns:
Value of the cumulative distribution function evaluated at x.
"""
return stats.rice.cdf(x, self.nu / self.sigma)
[docs]
def expected_value(self) -> float:
"""Returns the expected value of the Rician distribution."""
arg = -self.nu**2 / (2 * self.sigma**2)
return self.sigma * np.sqrt(np.pi / 2) * laguerre(arg, 1 / 2)
[docs]
def variance(self) -> float:
"""Returns the variance of the Rician distribution."""
arg = -self.nu**2 / (2 * self.sigma**2)
return (
2 * self.sigma**2
+ self.nu**2
- ((np.pi * self.sigma**2 / 2) * (laguerre(arg, 1 / 2) ** 2))
)
[docs]
def rms_value(self) -> float:
"""Returns the RMS value of the Rician distribution."""
return self.sigma * np.sqrt(2 + np.pi / 2)
[docs]
def get_samples(
self, size: Union[int, Tuple[int, ...]], seed: int = None
) -> NDArrayFloat:
"""Generate random variables from the Rician distribution.
Args:
size: Nnumber of random variables to generate.
seed: Seed for the random number generator.
Returns:
An array of size `size` containing random variables from the Rician
distribution.
"""
return np.array(
stats.rice.rvs(
self.nu / self.sigma, scale=self.sigma, size=size, random_state=seed
)
)
__all__ = ["Rician"]