diff --git a/src/pysatl_core/families/builtins/continuous/exponential.py b/src/pysatl_core/families/builtins/continuous/exponential.py index 8955219..cf128df 100644 --- a/src/pysatl_core/families/builtins/continuous/exponential.py +++ b/src/pysatl_core/families/builtins/continuous/exponential.py @@ -162,6 +162,28 @@ def char_func(parameters: Parametrization, t: NumericArray) -> ComplexArray: ) return cast(ComplexArray, result) + def lpdf(parameters: Parametrization, x: NumericArray) -> NumericArray: + """ + Logarithm of the probability density function for exponential distribution. + + Parameters + ---------- + parameters : Parametrization + Distribution parameters object with field: + - lambda_: float (rate parameter) + x : NumericArray + Points at which to evaluate the log-probability density function + + Returns + ------- + NumericArray + Log-probability density values at points x. + For x < 0 returns -np.inf. + """ + parameters = cast(_Rate, parameters) + lambda_ = parameters.lambda_ + return np.where(x >= 0, np.log(lambda_) - lambda_ * x, -np.inf) + def mean_func(parameters: Parametrization) -> float: """Mean of exponential distribution.""" parameters = cast(_Rate, parameters) @@ -210,6 +232,7 @@ def _support(_: Parametrization) -> ContinuousSupport: CharacteristicName.CDF: cdf, CharacteristicName.PPF: ppf, CharacteristicName.CF: char_func, + CharacteristicName.LPDF: lpdf, CharacteristicName.MEAN: mean_func, CharacteristicName.VAR: var_func, CharacteristicName.SKEW: skew_func, diff --git a/src/pysatl_core/families/builtins/continuous/normal.py b/src/pysatl_core/families/builtins/continuous/normal.py index 8ab3a2f..e435df7 100644 --- a/src/pysatl_core/families/builtins/continuous/normal.py +++ b/src/pysatl_core/families/builtins/continuous/normal.py @@ -165,6 +165,34 @@ def char_func(parameters: Parametrization, t: NumericArray) -> ComplexArray: mu = parameters.mu return cast(ComplexArray, np.exp(1j * mu * t - 0.5 * (sigma**2) * (t**2))) + def lpdf(parameters: Parametrization, x: NumericArray) -> NumericArray: + """ + Logarithm of the probability density function for normal distribution. + + Parameters + ---------- + parameters : Parametrization + Distribution parameters object with fields: + - mu: float (mean) + - sigma: float (standard deviation) + x : NumericArray + Points at which to evaluate the log-probability density function + + Returns + ------- + NumericArray + Log-probability density values at points x + """ + parameters = cast(_MeanStd, parameters) + sigma = parameters.sigma + mu = parameters.mu + + # log pdf = -0.5*log(2π) - log(σ) - (x-μ)²/(2σ²) + return cast( + NumericArray, + -0.5 * np.log(2 * np.pi) - np.log(sigma) - ((x - mu) ** 2) / (2 * sigma**2), + ) + def mean_func(parameters: Parametrization) -> float: """Mean of normal distribution.""" parameters = cast(_MeanStd, parameters) @@ -213,6 +241,7 @@ def _support(_: Parametrization) -> ContinuousSupport: CharacteristicName.CDF: cdf, CharacteristicName.PPF: ppf, CharacteristicName.CF: char_func, + CharacteristicName.LPDF: lpdf, CharacteristicName.MEAN: mean_func, CharacteristicName.VAR: var_func, CharacteristicName.SKEW: skew_func, diff --git a/src/pysatl_core/families/builtins/continuous/uniform.py b/src/pysatl_core/families/builtins/continuous/uniform.py index 6de0398..cd8dadc 100644 --- a/src/pysatl_core/families/builtins/continuous/uniform.py +++ b/src/pysatl_core/families/builtins/continuous/uniform.py @@ -188,6 +188,30 @@ def char_func(parameters: Parametrization, t: NumericArray) -> ComplexArray: return cast(ComplexArray, sinc_val * np.exp(1j * center * t_arr)) + def lpdf(parameters: Parametrization, x: NumericArray) -> NumericArray: + """ + Logarithm of the probability density function for uniform distribution. + + 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 log-probability density function + + Returns + ------- + NumericArray + Log-probability density values at points x + For x outside [lower_bound, upper_bound] returns -np.inf + """ + parameters = cast(_Standard, parameters) + a = parameters.lower_bound + b = parameters.upper_bound + return np.where((x >= a) & (x <= b), -np.log(b - a), -np.inf) + def mean_func(parameters: Parametrization) -> float: """Mean of uniform distribution.""" parameters = cast(_Standard, parameters) @@ -245,6 +269,7 @@ def _support(parameters: Parametrization) -> ContinuousSupport: CharacteristicName.CDF: cdf, CharacteristicName.PPF: ppf, CharacteristicName.CF: char_func, + CharacteristicName.LPDF: lpdf, CharacteristicName.MEAN: mean_func, CharacteristicName.VAR: var_func, CharacteristicName.SKEW: skew_func, diff --git a/src/pysatl_core/types.py b/src/pysatl_core/types.py index 150c9f1..0e7bb4c 100644 --- a/src/pysatl_core/types.py +++ b/src/pysatl_core/types.py @@ -285,6 +285,7 @@ class CharacteristicName(StrEnum): CDF = "cdf" PPF = "ppf" PMF = "pmf" + LPDF = "lpdf" # unimplemented in graph yet CF = "cf" # unimplemented in graph yet SF = "sf" # unimplemented in graph yet MEAN = "mean" # unimplemented in graph yet diff --git a/tests/unit/families/builtins/continuous/test_exponential.py b/tests/unit/families/builtins/continuous/test_exponential.py index 157ddf7..ee65685 100644 --- a/tests/unit/families/builtins/continuous/test_exponential.py +++ b/tests/unit/families/builtins/continuous/test_exponential.py @@ -121,6 +121,7 @@ def test_analytical_computations_availability(self): CharacteristicName.CDF, CharacteristicName.PPF, CharacteristicName.CF, + CharacteristicName.LPDF, CharacteristicName.MEAN, CharacteristicName.VAR, CharacteristicName.SKEW, @@ -131,14 +132,30 @@ def test_analytical_computations_availability(self): @pytest.mark.parametrize( "char_name, test_data, scipy_func, scipy_kwargs", [ - (CharacteristicName.PDF, [-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], expon.pdf, {"scale": 2.0}), - (CharacteristicName.CDF, [-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], expon.cdf, {"scale": 2.0}), + ( + CharacteristicName.PDF, + [-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], + expon.pdf, + {"scale": 2.0}, + ), + ( + CharacteristicName.CDF, + [-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], + expon.cdf, + {"scale": 2.0}, + ), ( CharacteristicName.PPF, [0.001, 0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99, 0.999], expon.ppf, {"scale": 2.0}, ), + ( + CharacteristicName.LPDF, + [-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], + expon.logpdf, + {"scale": 2.0}, + ), ], ) def test_array_input_for_characteristics(self, char_name, test_data, scipy_func, scipy_kwargs): diff --git a/tests/unit/families/builtins/continuous/test_normal.py b/tests/unit/families/builtins/continuous/test_normal.py index 164ca35..857ac65 100644 --- a/tests/unit/families/builtins/continuous/test_normal.py +++ b/tests/unit/families/builtins/continuous/test_normal.py @@ -149,6 +149,7 @@ def test_analytical_computations_availability(self): CharacteristicName.CDF, CharacteristicName.PPF, CharacteristicName.CF, + CharacteristicName.LPDF, CharacteristicName.MEAN, CharacteristicName.VAR, CharacteristicName.SKEW, @@ -177,6 +178,12 @@ def test_analytical_computations_availability(self): norm.ppf, {"loc": 2.0, "scale": 1.5}, ), + ( + CharacteristicName.LPDF, + [-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], + norm.logpdf, + {"loc": 2.0, "scale": 1.5}, + ), ], ) def test_array_input_for_characteristics(self, char_name, test_data, scipy_func, scipy_kwargs): diff --git a/tests/unit/families/builtins/continuous/test_uniform.py b/tests/unit/families/builtins/continuous/test_uniform.py index e3864c2..ba3df5a 100644 --- a/tests/unit/families/builtins/continuous/test_uniform.py +++ b/tests/unit/families/builtins/continuous/test_uniform.py @@ -148,6 +148,7 @@ def test_analytical_computations_availability(self): CharacteristicName.CDF, CharacteristicName.PPF, CharacteristicName.CF, + CharacteristicName.LPDF, CharacteristicName.MEAN, CharacteristicName.VAR, CharacteristicName.SKEW, @@ -176,6 +177,12 @@ def test_analytical_computations_availability(self): uniform.ppf, {"loc": 2.0, "scale": 3.0}, ), + ( + CharacteristicName.LPDF, + [1.0, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0], + uniform.logpdf, + {"loc": 2.0, "scale": 3.0}, + ), ], ) def test_array_input_for_characteristics(self, char_name, test_data, scipy_func, scipy_kwargs):