Source code for pysatl_core.families.builtins.continuous.exponential
"""
Exponential distribution family implementation.
Contains the Exponential family with rate and scale parameterizations.
"""
from __future__ import annotations
__author__ = "Fedor Myznikov"
__copyright__ = "Copyright (c) 2025 PySATL project"
__license__ = "SPDX-License-Identifier: MIT"
from typing import TYPE_CHECKING, cast
import numpy as np
from pysatl_core.distributions.support import ContinuousSupport
from pysatl_core.families.parametric_family import ParametricFamily
from pysatl_core.families.parametrizations import (
Parametrization,
constraint,
parametrization,
)
from pysatl_core.families.registry import ParametricFamilyRegister
from pysatl_core.types import (
CharacteristicName,
ComplexArray,
FamilyName,
NumericArray,
UnivariateContinuous,
)
if TYPE_CHECKING:
from typing import Any
[docs]
def configure_exponential_family() -> None:
"""
Configure and register the Exponential distribution family.
"""
if ParametricFamilyRegister.contains(FamilyName.EXPONENTIAL):
return
EXPONENTIAL_DOC = """
Exponential distribution.
The exponential distribution is a continuous probability distribution that
describes the time between events in a Poisson process. It has a single
parameter: rate (λ) or scale (β = 1/λ).
Probability density function (rate parametrization):
f(x) = λ * exp(-λ * x) for x ≥ 0
The exponential distribution is memoryless and is widely used in reliability
engineering, queuing theory, and survival analysis.
"""
def pdf(parameters: Parametrization, x: NumericArray) -> NumericArray:
"""
Probability density function for exponential distribution.
Parameters
----------
parameters : Parametrization
Distribution parameters object with fields:
- lambda_: float (rate parameter)
x : NumericArray
Points at which to evaluate the probability density function
Returns
-------
NumericArray
Probability density values at points x
"""
parameters = cast(_Rate, parameters)
lambda_ = parameters.lambda_
return np.where(x >= 0, lambda_ * np.exp(-lambda_ * x), 0.0)
def cdf(parameters: Parametrization, x: NumericArray) -> NumericArray:
"""
Cumulative distribution function for exponential distribution.
Parameters
----------
parameters : Parametrization
Distribution parameters object with fields:
- lambda_: float (rate parameter)
x : NumericArray
Points at which to evaluate the cumulative distribution function
Returns
-------
NumericArray
Probabilities P(X ≤ x) for each point x
"""
parameters = cast(_Rate, parameters)
lambda_ = parameters.lambda_
return np.where(x >= 0, 1.0 - np.exp(-lambda_ * x), 0.0)
def ppf(parameters: Parametrization, p: NumericArray) -> NumericArray:
"""
Percent point function (inverse CDF) for exponential distribution.
Parameters
----------
parameters : Parametrization
Distribution parameters object with fields:
- lambda_: float (rate parameter)
p : NumericArray
Probability from [0, 1]
Returns
-------
NumericArray
Quantiles corresponding to probabilities p:
- For p = 0: returns 0.0
- For p = 1: returns np.inf
- For p in (0, 1): returns -ln(1-p)/λ
Raises
------
ValueError
If probability is outside [0, 1]
"""
if np.any((p < 0) | (p > 1)):
raise ValueError("Probability must be in [0, 1]")
parameters = cast(_Rate, parameters)
lambda_ = parameters.lambda_
with np.errstate(divide="ignore", invalid="ignore"):
return np.where(p < 1.0, -np.log1p(-p) / lambda_, np.inf)
def char_func(parameters: Parametrization, t: NumericArray) -> ComplexArray:
"""
Characteristic function of exponential distribution.
Parameters
----------
parameters : Parametrization
Distribution parameters object with fields:
- lambda_: float (rate parameter)
t : NumericArray
Points at which to evaluate the characteristic function
Returns
-------
ComplexArray
Characteristic function values at points t
"""
CALCULATION_PRECISION = 1e-10
parameters = cast(_Rate, parameters)
lambda_ = parameters.lambda_
t_arr = np.asarray(t, dtype=np.float64)
denominator = lambda_ - 1j * t_arr
result = np.where(
np.abs(t_arr) < CALCULATION_PRECISION,
1.0 + 0j,
lambda_ / denominator,
)
return cast(ComplexArray, result)
def mean_func(parameters: Parametrization, _: Any) -> float:
"""Mean of exponential distribution."""
parameters = cast(_Rate, parameters)
return 1.0 / parameters.lambda_
def var_func(parameters: Parametrization, _: Any) -> float:
"""Variance of exponential distribution."""
parameters = cast(_Rate, parameters)
return 1.0 / (parameters.lambda_**2)
def skew_func(_1: Parametrization, _2: Any) -> float:
"""Skewness of exponential distribution (always 2)."""
return 2.0
def kurt_func(_1: Parametrization, _2: Any, excess: bool = False) -> float:
"""Raw or excess kurtosis of exponential distribution.
Parameters
----------
_1 : Parametrization
Needed by architecture parameter
excess : bool
A value defines if there will be raw or excess kurtosis
default is False
Returns
-------
float
Kurtosis value
"""
if not excess:
return 9.0
else:
return 6.0
def _support(_: Parametrization) -> ContinuousSupport:
"""Support of exponential distribution"""
return ContinuousSupport(left=0.0)
Exponential = ParametricFamily(
name=FamilyName.EXPONENTIAL,
distr_type=UnivariateContinuous,
distr_parametrizations=["rate", "scale"],
distr_characteristics={
CharacteristicName.PDF: pdf,
CharacteristicName.CDF: cdf,
CharacteristicName.PPF: ppf,
CharacteristicName.CF: char_func,
CharacteristicName.MEAN: mean_func,
CharacteristicName.VAR: var_func,
CharacteristicName.SKEW: skew_func,
CharacteristicName.KURT: kurt_func,
},
support_by_parametrization=_support,
)
Exponential.__doc__ = EXPONENTIAL_DOC
@parametrization(family=Exponential, name="rate")
class _Rate(Parametrization):
"""
Rate parametrization of exponential distribution.
Parameters
----------
lambda_ : float
Rate parameter (λ) of the distribution
"""
lambda_: float
@constraint(description="lambda_ > 0")
def check_lambda_positive(self) -> bool:
"""Check that rate parameter is positive."""
return self.lambda_ > 0
@parametrization(family=Exponential, name="scale")
class _Scale(Parametrization):
"""
Scale parametrization of exponential distribution.
Parameters
----------
beta : float
Scale parameter (β) of the distribution, β = 1/λ
"""
beta: float
@constraint(description="beta > 0")
def check_beta_positive(self) -> bool:
"""Check that scale parameter is positive."""
return self.beta > 0
def transform_to_base_parametrization(self) -> Parametrization:
"""
Transform to Rate parametrization.
Returns
-------
Parametrization
Rate parametrization instance
"""
return _Rate(lambda_=1.0 / self.beta)
ParametricFamilyRegister.register(Exponential)