Source code for pysatl_core.families.builtins.continuous.uniform

"""
Uniform distribution family implementation.

Contains the Uniform family with multiple 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_uniform_family() -> None: """ Configure and register the Uniform distribution family. """ if ParametricFamilyRegister.contains(FamilyName.CONTINUOUS_UNIFORM): return UNIFORM_DOC = """ Uniform (continuous) distribution. The uniform distribution is a continuous probability distribution where all intervals of the same length are equally probable. It is defined by two parameters: lower bound and upper bound. Probability density function: f(x) = 1/(upper_bound - lower_bound) for x in [lower_bound, upper_bound], 0 otherwise The uniform distribution is often used when there is no prior knowledge about the possible values of a variable, representing maximum uncertainty. """ def pdf(parameters: Parametrization, x: NumericArray) -> NumericArray: """ Probability density function for uniform distribution. - For x < lower_bound: returns 0 - For x > upper_bound: returns 0 - Otherwise: returns (1 / (upper_bound - lower_bound)) Parameters ---------- parameters : Parametrization Distribution parameters object with fields: - lower_bound: float (lower bound) - upper_bound: float (upper bound) x : NumericArray Points at which to evaluate the probability density function Returns ------- NumericArray Probability density values at points x """ parameters = cast(_Standard, parameters) lower_bound = parameters.lower_bound upper_bound = parameters.upper_bound return np.where( (x >= lower_bound) & (x <= upper_bound), 1.0 / (upper_bound - lower_bound), 0.0 ) def cdf(parameters: Parametrization, x: NumericArray) -> NumericArray: """ Cumulative distribution function for uniform distribution. Uses np.clip for vectorized computation: - For x < lower_bound: returns 0 - For x > upper_bound: returns 1 Parameters ---------- parameters : Parametrization Distribution parameters object with fields: - lower_bound: float (lower bound) - upper_bound: float (upper bound) x : NumericArray Points at which to evaluate the cumulative distribution function Returns ------- NumericArray Probabilities P(X ≤ x) for each point x """ parameters = cast(_Standard, parameters) lower_bound = parameters.lower_bound upper_bound = parameters.upper_bound return cast( NumericArray, np.clip((x - lower_bound) / (upper_bound - lower_bound), 0.0, 1.0) ) def ppf(parameters: Parametrization, p: NumericArray) -> NumericArray: """ Percent point function (inverse CDF) for uniform distribution. For uniform distribution on [lower_bound, upper_bound]: - For p = 0: returns lower_bound - For p = 1: returns upper_bound - For p in (0, 1): returns lower_bound + p × (upper_bound - lower_bound) Parameters ---------- parameters : Parametrization Distribution parameters object with fields: - lower_bound: float (lower bound) - upper_bound: float (upper bound) p : NumericArray Probability from [0, 1] Returns ------- NumericArray Quantiles corresponding to probabilities 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(_Standard, parameters) lower_bound = parameters.lower_bound upper_bound = parameters.upper_bound return cast(NumericArray, lower_bound + p * (upper_bound - lower_bound)) def char_func(parameters: Parametrization, t: NumericArray) -> ComplexArray: """ Characteristic function of uniform distribution. Characteristic function formula for uniform distribution on [lower_bound, upper bound]: φ(t) = sinc((upper bound - lower_bound) * t / 2) * * exp(i * (lower_bound + upper_bound) * t / 2) where sinc(x) = sin(πx)/(πx) as defined by numpy. Parameters ---------- parameters : Parametrization Distribution parameters object with fields: - lower_bound: float (lower bound) - upper_bound: float (upper bound) t : NumericArray Points at which to evaluate the characteristic function Returns ------- ComplexArray Characteristic function values at points t """ parameters = cast(_Standard, parameters) lower_bound = parameters.lower_bound upper_bound = parameters.upper_bound width = upper_bound - lower_bound center = (lower_bound + upper_bound) / 2 t_arr = np.asarray(t, dtype=np.float64) x = width * t_arr / (2 * np.pi) sinc_val = np.sinc(x) return cast(ComplexArray, sinc_val * np.exp(1j * center * t_arr)) def mean_func(parameters: Parametrization, _: Any) -> float: """Mean of uniform distribution.""" parameters = cast(_Standard, parameters) return (parameters.lower_bound + parameters.upper_bound) / 2 def var_func(parameters: Parametrization, _: Any) -> float: """Variance of uniform distribution.""" parameters = cast(_Standard, parameters) width = parameters.upper_bound - parameters.lower_bound return width**2 / 12 def skew_func(_1: Parametrization, _2: Any) -> int: """Skewness of uniform distribution (always 0).""" return 0 def kurt_func(_1: Parametrization, _2: Any, excess: bool = False) -> float: """Raw or excess kurtosis of uniform distribution. Parameters ---------- _1 : Parametrization Needed by architecture parameter _2 : Any 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 1.8 else: return -1.2 def _support(parameters: Parametrization) -> ContinuousSupport: """Support of uniform distribution""" parameters = cast(_Standard, parameters.transform_to_base_parametrization()) return ContinuousSupport( left=parameters.lower_bound, right=parameters.upper_bound, left_closed=True, right_closed=True, ) Uniform = ParametricFamily( name=FamilyName.CONTINUOUS_UNIFORM, distr_type=UnivariateContinuous, distr_parametrizations=["standard", "meanWidth", "minRange"], 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, ) Uniform.__doc__ = UNIFORM_DOC @parametrization(family=Uniform, name="standard") class _Standard(Parametrization): """ Standard parametrization of uniform distribution. Parameters ---------- lower_bound : float Lower bound of the distribution upper_bound : float Upper bound of the distribution """ lower_bound: float upper_bound: float @constraint(description="lower_bound < upper_bound") def check_lower_less_than_upper(self) -> bool: """Check that lower bound is less than upper bound.""" return self.lower_bound < self.upper_bound @parametrization(family=Uniform, name="meanWidth") class _MeanWidth(Parametrization): """ Mean-width parametrization of uniform distribution. Parameters ---------- mean : float Mean (center) of the distribution width : float Width of the distribution (upper_bound - lower_bound) """ mean: float width: float @constraint(description="width > 0") def check_width_positive(self) -> bool: """Check that width is positive.""" return self.width > 0 def transform_to_base_parametrization(self) -> Parametrization: """ Transform to Standard parametrization. Returns ------- Parametrization Standard parametrization instance """ half_width = self.width / 2 return _Standard(lower_bound=self.mean - half_width, upper_bound=self.mean + half_width) @parametrization(family=Uniform, name="minRange") class _MinRange(Parametrization): """ Minimum-range parametrization of uniform distribution. Parameters ---------- minimum : float Minimum value (lower bound) range_val : float Range of the distribution (upper_bound - lower_bound) """ minimum: float range_val: float @constraint(description="range_val > 0") def check_range_positive(self) -> bool: """Check that range is positive.""" return self.range_val > 0 def transform_to_base_parametrization(self) -> Parametrization: """ Transform to Standard parametrization. Returns ------- Parametrization Standard parametrization instance """ return _Standard(lower_bound=self.minimum, upper_bound=self.minimum + self.range_val) ParametricFamilyRegister.register(Uniform)