Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b9539ab
implemented custom pdf, cdf, icdf method for Uniform.py
connor-krill Jun 2, 2023
f98071a
implemented custom pdf, cdf method for Normal.py
connor-krill Jun 2, 2023
8db235f
new validation type for NumericArrayLikes and appropriate tests for n…
connor-krill Jun 2, 2023
b3907b9
added scratch folder
connor-krill Jun 2, 2023
2f895b4
removed inverse cdf for normal bc it was slow and inaccurate
connor-krill Sep 1, 2023
83058db
Merge remote-tracking branch 'origin/Development' into feature/distri…
connor-krill Sep 1, 2023
ce7ccad
Merge branch 'Development' into feature/distribution_speed_up
dimtsap Sep 2, 2023
fc0dac7
renamed variable input for consistency and to pass unit tests
connor-krill Sep 5, 2023
1e91dc8
Merge remote-tracking branch 'origin/feature/distribution_speed_up' i…
connor-krill Sep 5, 2023
8114bcd
changed return behavior for floats and ints in custom pdf, cdf, icdf …
connor-krill Sep 12, 2023
d65483b
improved documentation, imports, and beartype
connor-krill May 22, 2024
7c3bdc6
moved tests for custom uniform and normal pdf, cdf, icdf to test_norm…
connor-krill May 22, 2024
c5f1783
cleaned up formatting
connor-krill May 22, 2024
26ff00e
added custom inverse function
connor-krill May 28, 2024
298ccb6
added int as allowed output type
connor-krill May 28, 2024
d10e00f
improved tests
connor-krill May 28, 2024
fbd3aa7
added tests for not-a-number and infinity
connor-krill Oct 15, 2024
290a3cf
added __repr__ method
connor-krill Mar 5, 2025
bdb68d3
added tests for __repr__ method
connor-krill Mar 5, 2025
5258497
added tests for custom pdf, cdf, icdf methods
connor-krill Mar 5, 2025
ce275bf
minor improvements to docstrings
connor-krill Mar 17, 2025
7cb5f53
Merge branch 'Development' into feature/distribution_speed_up
connor-krill Mar 18, 2025
60d56dc
fixed how types are handled in `if` statement
connor-krill Mar 18, 2025
98149f8
Merge remote-tracking branch 'origin/feature/distribution_speed_up' i…
connor-krill Mar 18, 2025
36c33d8
Merge branch 'Development' into feature/distribution_speed_up
connor-krill May 19, 2025
29fe99a
added Triangular distribution to address issue #262
connor-krill May 29, 2025
bcf6122
Merge remote-tracking branch 'origin/feature/distribution_speed_up' i…
connor-krill May 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,4 @@ logo/
/python_model_third_party.py
/third_party_script.py
/docs/source/auto_examples/
scratch/
9 changes: 8 additions & 1 deletion src/UQpy/distributions/collection/Beta.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from typing import Union

import scipy.stats as stats
from UQpy.distributions.baseclass import DistributionContinuous1D
from beartype import beartype
Expand Down Expand Up @@ -30,3 +29,11 @@ def __init__(
ordered_parameters=("a", "b", "loc", "scale"),
)
self._construct_from_scipy(scipy_name=stats.beta)

def __repr__(self):
s = "{a}, {b}"
if self.parameters["loc"] != 0.0:
s += ", loc={loc}"
if self.parameters["scale"] != 1.0:
s += ", scale={scale}"
return "Beta(" + s.format(**self.parameters) + ")"
6 changes: 6 additions & 0 deletions src/UQpy/distributions/collection/Binomial.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ def __init__(
"""
super().__init__(n=n, p=p, loc=loc, ordered_parameters=("n", "p", "loc"))
self._construct_from_scipy(scipy_name=stats.binom)

def __repr__(self):
s = "{n}, {p}"
if self.parameters["loc"] != 0.0:
s += ", loc={loc}"
return "Binomial(" + s.format(**self.parameters) + ")"
9 changes: 9 additions & 0 deletions src/UQpy/distributions/collection/Cauchy.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ def __init__(
"""
super().__init__(loc=loc, scale=scale, ordered_parameters=("loc", "scale"))
self._construct_from_scipy(scipy_name=stats.cauchy)

def __repr__(self):
s = []
if self.parameters["loc"] != 0.0:
s.append("loc={loc}")
if self.parameters["scale"] != 1.0:
s.append("scale={scale}")
s = ", ".join(s)
return "Cauchy(" + s.format(**self.parameters) + ")"
8 changes: 8 additions & 0 deletions src/UQpy/distributions/collection/ChiSquare.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,11 @@ def __init__(
df=df, loc=loc, scale=scale, ordered_parameters=("df", "loc", "scale")
)
self._construct_from_scipy(scipy_name=stats.chi2)

def __repr__(self):
s = "{df}"
if self.parameters["loc"] != 0.0:
s += ", loc={loc}"
if self.parameters["scale"] != 1.0:
s += ", scale={scale}"
return "ChiSquare(" + s.format(**self.parameters) + ")"
9 changes: 9 additions & 0 deletions src/UQpy/distributions/collection/Exponential.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ def __init__(
"""
super().__init__(loc=loc, scale=scale, ordered_parameters=("loc", "scale"))
self._construct_from_scipy(scipy_name=stats.expon)

def __repr__(self):
s = []
if self.parameters["loc"] != 0.0:
s.append("loc={loc}")
if self.parameters["scale"] != 1.0:
s.append("scale={scale}")
s = ", ".join(s)
return "Exponential(" + s.format(**self.parameters) + ")"
8 changes: 8 additions & 0 deletions src/UQpy/distributions/collection/Gamma.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ def __init__(
a=a, loc=loc, scale=scale, ordered_parameters=("a", "loc", "scale")
)
self._construct_from_scipy(scipy_name=stats.gamma)

def __repr__(self):
s = "{a}"
if self.parameters["loc"] != 0.0:
s += ", loc={loc}"
if self.parameters["scale"] != 1.0:
s += ", scale={scale}"
return "Gamma(" + s.format(**self.parameters) + ")"
8 changes: 8 additions & 0 deletions src/UQpy/distributions/collection/GeneralizedExtreme.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ def __init__(
c=c, loc=loc, scale=scale, ordered_parameters=("c", "loc", "scale")
)
self._construct_from_scipy(scipy_name=stats.genextreme)

def __repr__(self):
s = "{c}"
if self.parameters["loc"] != 0.0:
s += ", loc={loc}"
if self.parameters["scale"] != 1.0:
s += ", scale={scale}"
return "GeneralizedExtreme(" + s.format(**self.parameters) + ")"
8 changes: 8 additions & 0 deletions src/UQpy/distributions/collection/InverseGaussian.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ def __init__(
mu=mu, loc=loc, scale=scale, ordered_parameters=("mu", "loc", "scale")
)
self._construct_from_scipy(scipy_name=stats.invgauss)

def __repr__(self):
s = "{mu}"
if self.parameters["loc"] != 0.0:
s += ", loc={loc}"
if self.parameters["scale"] != 1.0:
s += ", scale={scale}"
return "InverseGauss(" + s.format(**self.parameters) + ")"
3 changes: 3 additions & 0 deletions src/UQpy/distributions/collection/JointCopula.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,6 @@ def update_parameters(self, **kwargs: dict):
self.copula.parameters[key] = value
else:
self.marginals[int(index)].parameters[key] = value

def __repr__(self):
return f"JointCopula({self.marginals}, {self.copula})"
39 changes: 29 additions & 10 deletions src/UQpy/distributions/collection/JointIndependent.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
class JointIndependent(DistributionND):
@beartype
def __init__(
self,
marginals: Union[list[DistributionContinuous1D], list[DistributionDiscrete1D]],
self,
marginals: Union[list[DistributionContinuous1D], list[DistributionDiscrete1D]],
):
"""
:param marginals: list of distribution objects that define the marginals.
Expand All @@ -24,12 +24,20 @@ def __init__(
self.ordered_parameters = []
for i, m in enumerate(marginals):
self.ordered_parameters.extend(
[key + "_" + str(i) for key in m.ordered_parameters])
[key + "_" + str(i) for key in m.ordered_parameters]
)

# Check and save the marginals
if not (isinstance(marginals, list)
and all(isinstance(d, (DistributionContinuous1D, DistributionDiscrete1D)) for d in marginals)):
raise ValueError("Input marginals must be a list of Distribution1d objects.")
if not (
isinstance(marginals, list)
and all(
isinstance(d, (DistributionContinuous1D, DistributionDiscrete1D))
for d in marginals
)
):
raise ValueError(
"Input marginals must be a list of Distribution1d objects."
)
self.marginals = marginals

# If all marginals have a method, the joint has it to
Expand Down Expand Up @@ -70,6 +78,7 @@ def joint_log_pdf(dist, x):
self.log_pmf = MethodType(joint_log_pdf, self)

if all(hasattr(m, "cdf") for m in self.marginals):

def joint_cdf(dist, x):
x = dist.check_x_dimension(x)
# Compute cdf of independent marginals
Expand Down Expand Up @@ -107,8 +116,8 @@ def joint_fit(dist, data):
mle_all = {}
for ind_m, marg in enumerate(dist.marginals):
if any(
param_value is None
for param_value in marg.get_parameters().values()
param_value is None
for param_value in marg.get_parameters().values()
):
mle_i = marg.fit(data[:, ind_m])
else:
Expand All @@ -125,8 +134,15 @@ def joint_fit(dist, data):
def joint_moments(dist, moments2return="mvsk"):
# Go through all marginals
if len(moments2return) == 1:
return np.array([marg.moments(moments2return=moments2return) for marg in dist.marginals])
moments_ = [np.empty((len(dist.marginals),)) for _ in range(len(moments2return))]
return np.array(
[
marg.moments(moments2return=moments2return)
for marg in dist.marginals
]
)
moments_ = [
np.empty((len(dist.marginals),)) for _ in range(len(moments2return))
]
for ind_m, marg in enumerate(dist.marginals):
moments_i = marg.moments(moments2return=moments2return)
for j in range(len(moments2return)):
Expand Down Expand Up @@ -174,3 +190,6 @@ def update_parameters(self, **kwargs: dict):
key_split = key_indexed.split("_")
key, index = "_".join(key_split[:-1]), int(key_split[-1])
self.marginals[index].parameters[key] = value

def __repr__(self):
return f"JointIndependent({self.marginals})"
9 changes: 9 additions & 0 deletions src/UQpy/distributions/collection/Laplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ def __init__(
"""
super().__init__(loc=loc, scale=scale, ordered_parameters=("loc", "scale"))
self._construct_from_scipy(scipy_name=stats.laplace)

def __repr__(self):
s = []
if self.parameters["loc"] != 0.0:
s.append("loc={loc}")
if self.parameters["scale"] != 1.0:
s.append("scale={scale}")
s = ", ".join(s)
return "Laplace(" + s.format(**self.parameters) + ")"
9 changes: 9 additions & 0 deletions src/UQpy/distributions/collection/Levy.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ def __init__(
"""
super().__init__(loc=loc, scale=scale, ordered_parameters=("loc", "scale"))
self._construct_from_scipy(scipy_name=stats.levy)

def __repr__(self):
s = []
if self.parameters["loc"] != 0.0:
s.append("loc={loc}")
if self.parameters["scale"] != 1.0:
s.append("scale={scale}")
s = ", ".join(s)
return "Levy(" + s.format(**self.parameters) + ")"
9 changes: 9 additions & 0 deletions src/UQpy/distributions/collection/Logistic.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ def __init__(
"""
super().__init__(loc=loc, scale=scale, ordered_parameters=("loc", "scale"))
self._construct_from_scipy(scipy_name=stats.logistic)

def __repr__(self):
s = []
if self.parameters["loc"] != 0.0:
s.append("loc={loc}")
if self.parameters["scale"] != 1.0:
s.append("scale={scale}")
s = ", ".join(s)
return "Logistic(" + s.format(**self.parameters) + ")"
8 changes: 8 additions & 0 deletions src/UQpy/distributions/collection/Lognormal.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ def __init__(
s=s, loc=loc, scale=scale, ordered_parameters=("s", "loc", "scale")
)
self._construct_from_scipy(scipy_name=stats.lognorm)

def __repr__(self):
s = "{s}"
if self.parameters["loc"] != 0.0:
s += ", loc={loc}"
if self.parameters["scale"] != 1.0:
s += ", scale={scale}"
return "Lognormal(" + s.format(**self.parameters) + ")"
9 changes: 9 additions & 0 deletions src/UQpy/distributions/collection/Maxwell.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ def __init__(
"""
super().__init__(loc=loc, scale=scale, ordered_parameters=("loc", "scale"))
self._construct_from_scipy(scipy_name=stats.maxwell)

def __repr__(self):
s = []
if self.parameters["loc"] != 0.0:
s.append("loc={loc}")
if self.parameters["scale"] != 1.0:
s.append("scale={scale}")
s = ", ".join(s)
return "Maxwell(" + s.format(**self.parameters) + ")"
3 changes: 3 additions & 0 deletions src/UQpy/distributions/collection/Multinomial.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,6 @@ def moments(self, moments2return="mv"):
return mean, cov
else:
raise ValueError('UQpy: moments2return must be "m", "v" or "mv".')

def __repr__(self):
return "Multinomial({n}, {p})".format(**self.parameters)
23 changes: 19 additions & 4 deletions src/UQpy/distributions/collection/MultivariateNormal.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ def __init__(
if isinstance(cov, (int, float)):
pass
else:
if not (len(np.array(cov).shape) in [1, 2] and all(sh == len(mean) for sh in np.array(cov).shape)):
raise ValueError("Input covariance must be a float or ndarray of appropriate dimensions.")
if not (
len(np.array(cov).shape) in [1, 2]
and all(sh == len(mean) for sh in np.array(cov).shape)
):
raise ValueError(
"Input covariance must be a float or ndarray of appropriate dimensions."
)
super().__init__(mean=mean, cov=cov, ordered_parameters=["mean", "cov"])

def cdf(self, x):
Expand All @@ -44,8 +49,9 @@ def log_pdf(self, x):
def rvs(self, nsamples=1, random_state=None):
if not (isinstance(nsamples, int) and nsamples >= 1):
raise ValueError("Input nsamples must be an integer > 0.")
return stats.multivariate_normal.rvs(size=nsamples, random_state=random_state, **self.parameters
).reshape((nsamples, -1))
return stats.multivariate_normal.rvs(
size=nsamples, random_state=random_state, **self.parameters
).reshape((nsamples, -1))

def fit(self, data):
data = self.check_x_dimension(data)
Expand All @@ -65,3 +71,12 @@ def moments(self, moments2return="mv"):
return self.get_parameters()["mean"], self.get_parameters()["cov"]
else:
raise ValueError('UQpy: moments2return must be "m", "v" or "mv".')

def __repr__(self):
s = "{mean}"
is_float_or_int = isinstance(self.parameters["cov"], (int, float))
if is_float_or_int:
if self.parameters["cov"] == 1.0:
return "MultivariateNormal(" + s.format(**self.parameters) + ")"
s += ", cov={cov}"
return "MultivariateNormal(" + s.format(**self.parameters) + ")"
64 changes: 62 additions & 2 deletions src/UQpy/distributions/collection/Normal.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from typing import Union
import numpy as np
import scipy.stats as stats
from beartype import beartype
from scipy.special import erf, erfinv
from UQpy.distributions.baseclass import DistributionContinuous1D
from UQpy.utilities.ValidationTypes import NumericArrayLike


@beartype
class Normal(DistributionContinuous1D):

@beartype
def __init__(
self, loc: Union[None, float, int] = 0.0, scale: Union[None, float, int] = 1.0
):
Expand All @@ -17,3 +19,61 @@ def __init__(
"""
super().__init__(loc=loc, scale=scale, ordered_parameters=("loc", "scale"))
self._construct_from_scipy(scipy_name=stats.norm)
self.pdf = self.__probability_density_function
self.cdf = self.__cumulative_distribution_function
self.icdf = self.__inverse_cumulative_distribution_function

def __probability_density_function(
self, x: NumericArrayLike
) -> Union[float, np.ndarray]:
"""Probability density function for normal distribution

:param x: Points to evaluate pdf at
:return: PDF of ``x`` as defined by :math:`f_x(x)`
"""
x_array = np.atleast_1d(x)
mean = self.parameters["loc"]
standard_deviation = self.parameters["scale"]
normalizing_constant = 1 / (standard_deviation * np.sqrt(2 * np.pi))
pdf = normalizing_constant * np.exp(
-0.5 * ((x_array - mean) / standard_deviation) ** 2
)
return pdf

def __cumulative_distribution_function(
self, x: NumericArrayLike
) -> Union[float, np.ndarray]:
"""Cumulative distribution function for the normal distribution defined with the error function

:param x: Points to evaluate cdf at
:return: CDF of ``x`` as defined by :math:`F_X(x)`
"""
x_array = np.atleast_1d(x)
mean = self.parameters["loc"]
standard_deviation = self.parameters["scale"]
erf_input = (x_array - mean) / (standard_deviation * np.sqrt(2.0))
cdf = (1.0 + erf(erf_input)) / 2.0
return cdf

def __inverse_cumulative_distribution_function(
self, y: NumericArrayLike
) -> Union[float, np.ndarray]:
"""Compute the inverse CDF for the normal distribution with the inverse error function

:param y:
:return:
"""
y_array = np.atleast_1d(y)
mean = self.parameters["loc"]
standard_deviation = self.parameters["scale"]
normalized_icdf = erfinv((2 * y_array) - 1)
icdf = (normalized_icdf * standard_deviation * np.sqrt(2.0)) + mean
return icdf

def __repr__(self):
s = []
if self.parameters["loc"] != 0.0:
s.append(f"loc={self.parameters['loc']}")
if self.parameters["scale"] != 1.0:
s.append(f"scale={self.parameters['scale']}")
return "Normal(" + ", ".join(s) + ")"
8 changes: 8 additions & 0 deletions src/UQpy/distributions/collection/Pareto.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,11 @@ def __init__(
b=b, loc=loc, scale=scale, ordered_parameters=("b", "loc", "scale")
)
self._construct_from_scipy(scipy_name=stats.pareto)

def __repr__(self):
s = "{b}"
if self.parameters["loc"] != 0.0:
s += ", loc={loc}"
if self.parameters["scale"] != 1.0:
s += ", scale={scale}"
return "Pareto(" + s.format(**self.parameters) + ")"
Loading
Loading