CUQIpy 1.0.0.post0.dev337__py3-none-any.whl → 1.0.0.post0.dev352__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of CUQIpy might be problematic. Click here for more details.
- {CUQIpy-1.0.0.post0.dev337.dist-info → CUQIpy-1.0.0.post0.dev352.dist-info}/METADATA +1 -1
- {CUQIpy-1.0.0.post0.dev337.dist-info → CUQIpy-1.0.0.post0.dev352.dist-info}/RECORD +8 -7
- cuqi/_version.py +3 -3
- cuqi/distribution/__init__.py +1 -0
- cuqi/distribution/_modifiedhalfnormal.py +184 -0
- {CUQIpy-1.0.0.post0.dev337.dist-info → CUQIpy-1.0.0.post0.dev352.dist-info}/LICENSE +0 -0
- {CUQIpy-1.0.0.post0.dev337.dist-info → CUQIpy-1.0.0.post0.dev352.dist-info}/WHEEL +0 -0
- {CUQIpy-1.0.0.post0.dev337.dist-info → CUQIpy-1.0.0.post0.dev352.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: CUQIpy
|
|
3
|
-
Version: 1.0.0.post0.
|
|
3
|
+
Version: 1.0.0.post0.dev352
|
|
4
4
|
Summary: Computational Uncertainty Quantification for Inverse problems in Python
|
|
5
5
|
Maintainer-email: "Nicolai A. B. Riis" <nabr@dtu.dk>, "Jakob S. Jørgensen" <jakj@dtu.dk>, "Amal M. Alghamdi" <amaal@dtu.dk>, Chao Zhang <chaz@dtu.dk>
|
|
6
6
|
License: Apache License
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
cuqi/__init__.py,sha256=LsGilhl-hBLEn6Glt8S_l0OJzAA1sKit_rui8h-D-p0,488
|
|
2
2
|
cuqi/_messages.py,sha256=fzEBrZT2kbmfecBBPm7spVu7yHdxGARQB4QzXhJbCJ0,415
|
|
3
|
-
cuqi/_version.py,sha256=
|
|
3
|
+
cuqi/_version.py,sha256=g8tnztVUpVATX40RuxhlLPkPxb_ZLhy-dHUYqyHVXs8,510
|
|
4
4
|
cuqi/config.py,sha256=wcYvz19wkeKW2EKCGIKJiTpWt5kdaxyt4imyRkvtTRA,526
|
|
5
5
|
cuqi/diagnostics.py,sha256=5OrbJeqpynqRXOe5MtOKKhe7EAVdOEpHIqHnlMW9G_c,3029
|
|
6
6
|
cuqi/array/__init__.py,sha256=-EeiaiWGNsE3twRS4dD814BIlfxEsNkTCZUc5gjOXb0,30
|
|
@@ -14,7 +14,7 @@ cuqi/data/cookie.png,sha256=mr6wUeoIUc5VC2qYj8vafOmTbcRwz0fHz4IIPK9_PnE,984680
|
|
|
14
14
|
cuqi/data/satellite.mat,sha256=a0Nz_Ak-Y0m360dH74pa_rpk-MhaQ91ftGTKhQX7I8g,16373
|
|
15
15
|
cuqi/density/__init__.py,sha256=0zfVcPgqdqiPkss5n_WP_PUt-G3ovHXjokhqEKIlLwA,48
|
|
16
16
|
cuqi/density/_density.py,sha256=BG7gtP0cbFYLVgjYQGkNAhM95PR5ocBVLKRlOVX2PyM,7253
|
|
17
|
-
cuqi/distribution/__init__.py,sha256=
|
|
17
|
+
cuqi/distribution/__init__.py,sha256=qKt5uV5E988Zj3LIz0mgk3yICWKY2uoOq4vqLCETrdg,667
|
|
18
18
|
cuqi/distribution/_beta.py,sha256=hdAc6Tbuz9Yqf76NSHxpaUgN7s6Z2lNV7YSRD3JhyCU,2997
|
|
19
19
|
cuqi/distribution/_cauchy.py,sha256=UsVXYz8HhagXN5fIWSAIyELqhsJAX_-wk9kkRGgRmA8,3296
|
|
20
20
|
cuqi/distribution/_cmrf.py,sha256=tCbEulM_O7FB3C_W-3IqZp9zGHkTofCdFF0ybHc9UZI,3745
|
|
@@ -28,6 +28,7 @@ cuqi/distribution/_joint_distribution.py,sha256=jRsV1Dt-pW6sG_xNqF0TugeVKDJY4Kh5
|
|
|
28
28
|
cuqi/distribution/_laplace.py,sha256=5exLvlzJm2AgfvZ3KUSkjfwlGwwbsktBxP8z0iLMik8,1401
|
|
29
29
|
cuqi/distribution/_lmrf.py,sha256=rdGoQ-fPe1oW6Z29P-l3woq0NX3_RxUQ2rzm1VzemNM,3290
|
|
30
30
|
cuqi/distribution/_lognormal.py,sha256=st1Uhf67qy2Seo65hA88JQ7lkEjQkW6KxznXahF_0SU,2844
|
|
31
|
+
cuqi/distribution/_modifiedhalfnormal.py,sha256=gB9fj10hdMvr05aa_xhAirJATLqKh1gpbdSs2pRoAVM,7267
|
|
31
32
|
cuqi/distribution/_normal.py,sha256=UeoTtGDT7YSf4ZNo2amlVF9K-YQpYbf8q76jcRJTVFw,1914
|
|
32
33
|
cuqi/distribution/_posterior.py,sha256=zAfL0GECxekZ2lBt1W6_LN0U_xskMwK4VNce5xAF7ig,5018
|
|
33
34
|
cuqi/distribution/_uniform.py,sha256=7xJmCZH_LPhuGkwEDGh-_CTtzcWKrXMOxtTJUFb7Ydo,1607
|
|
@@ -81,8 +82,8 @@ cuqi/testproblem/_testproblem.py,sha256=x769LwwRdJdzIiZkcQUGb_5-vynNTNALXWKato7s
|
|
|
81
82
|
cuqi/utilities/__init__.py,sha256=T4tLsC215MknBCsw_C0Qeeg_ox26aDUrCA5hbWvNQkU,387
|
|
82
83
|
cuqi/utilities/_get_python_variable_name.py,sha256=QwlBVj2koJRA8s8pWd554p7-ElcI7HUwY32HknaR92E,1827
|
|
83
84
|
cuqi/utilities/_utilities.py,sha256=MWAqV6L5btMpWwlUzrZYuV2VeSpfTbOaLRMRkuw2WIA,8509
|
|
84
|
-
CUQIpy-1.0.0.post0.
|
|
85
|
-
CUQIpy-1.0.0.post0.
|
|
86
|
-
CUQIpy-1.0.0.post0.
|
|
87
|
-
CUQIpy-1.0.0.post0.
|
|
88
|
-
CUQIpy-1.0.0.post0.
|
|
85
|
+
CUQIpy-1.0.0.post0.dev352.dist-info/LICENSE,sha256=kJWRPrtRoQoZGXyyvu50Uc91X6_0XRaVfT0YZssicys,10799
|
|
86
|
+
CUQIpy-1.0.0.post0.dev352.dist-info/METADATA,sha256=NfPWIJpaMKeytArVU0GZCtQnlf87FNbAQkiOIdmA_ik,18393
|
|
87
|
+
CUQIpy-1.0.0.post0.dev352.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
88
|
+
CUQIpy-1.0.0.post0.dev352.dist-info/top_level.txt,sha256=AgmgMc6TKfPPqbjV0kvAoCBN334i_Lwwojc7HE3ZwD0,5
|
|
89
|
+
CUQIpy-1.0.0.post0.dev352.dist-info/RECORD,,
|
cuqi/_version.py
CHANGED
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2024-06-
|
|
11
|
+
"date": "2024-06-19T09:05:37+0200",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.0.0.post0.
|
|
14
|
+
"full-revisionid": "1e1deddbe7925206fdaef6a2d9bf8ce9e33ad73d",
|
|
15
|
+
"version": "1.0.0.post0.dev352"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
cuqi/distribution/__init__.py
CHANGED
|
@@ -3,6 +3,7 @@ from ._beta import Beta
|
|
|
3
3
|
from ._cauchy import Cauchy
|
|
4
4
|
from ._cmrf import CMRF
|
|
5
5
|
from ._gamma import Gamma
|
|
6
|
+
from ._modifiedhalfnormal import ModifiedHalfNormal
|
|
6
7
|
from ._gaussian import Gaussian, JointGaussianSqrtPrec
|
|
7
8
|
from ._gmrf import GMRF
|
|
8
9
|
from ._inverse_gamma import InverseGamma
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import scipy.stats as sps
|
|
3
|
+
import scipy.special as special
|
|
4
|
+
from cuqi.distribution import Distribution
|
|
5
|
+
from cuqi.utilities import force_ndarray
|
|
6
|
+
|
|
7
|
+
class ModifiedHalfNormal(Distribution):
|
|
8
|
+
"""
|
|
9
|
+
Represents a modified half-normal (MHN) distribution, a three-parameter family of distributions generalizing the Gamma distribution.
|
|
10
|
+
The distribution is continuous with pdf
|
|
11
|
+
f(x; alpha, beta, gamma) propto x^(alpha-1) * exp(-beta * x^2 + gamma * x)
|
|
12
|
+
|
|
13
|
+
The MHN generalizes the half-normal distribution, because
|
|
14
|
+
f(x; 1, beta, 0) propto exp(-beta * x^2)
|
|
15
|
+
|
|
16
|
+
The MHN generalizes the gamma distribution because
|
|
17
|
+
f(x; alpha, 0, -gamma) propto x^(alpha-1) * exp(- gamma * x)
|
|
18
|
+
|
|
19
|
+
Reference:
|
|
20
|
+
[1] Sun, et al. "The Modified-Half-Normal distribution: Properties and an efficient sampling scheme." Communications in Statistics-Theory and Methods
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
alpha : float
|
|
25
|
+
The polynomial exponent parameter of the MHN distribution. Must be positive.
|
|
26
|
+
|
|
27
|
+
beta : float
|
|
28
|
+
The quadratic exponential parameter of the MHN distribution. Must be positive.
|
|
29
|
+
|
|
30
|
+
gamma : float
|
|
31
|
+
The linear exponential parameter of the MHN distribution.
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
def __init__(self, alpha=None, beta=None, gamma=None, is_symmetric=False, **kwargs):
|
|
35
|
+
# Init from abstract distribution class
|
|
36
|
+
super().__init__(is_symmetric=is_symmetric, **kwargs)
|
|
37
|
+
|
|
38
|
+
self._alpha = alpha
|
|
39
|
+
self._beta = beta
|
|
40
|
+
self._gamma = gamma
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def alpha(self):
|
|
44
|
+
""" The polynomial exponent parameter of the MHN distribution. Must be positive. """
|
|
45
|
+
return self._alpha
|
|
46
|
+
|
|
47
|
+
@alpha.setter
|
|
48
|
+
def shape(self, value):
|
|
49
|
+
self._shape = force_ndarray(value, flatten=True)
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def beta(self):
|
|
53
|
+
""" The quadratic exponential parameter of the MHN distribution. Must be positive. """
|
|
54
|
+
return self._alpha
|
|
55
|
+
|
|
56
|
+
@beta.setter
|
|
57
|
+
def beta(self, value):
|
|
58
|
+
self._beta = force_ndarray(value, flatten=True)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def gamma(self):
|
|
62
|
+
""" The linear exponential parameter of the MHN distribution. """
|
|
63
|
+
return self._alpha
|
|
64
|
+
|
|
65
|
+
@gamma.setter
|
|
66
|
+
def gamma(self, value):
|
|
67
|
+
self._gamma = force_ndarray(value, flatten=True)
|
|
68
|
+
|
|
69
|
+
def logpdf(self, x): # Unnormalized
|
|
70
|
+
return np.sum((self.alpha - 1)*np.log(x) - self.beta * x * x + self.gamma * x)
|
|
71
|
+
|
|
72
|
+
def _gradient_scalar(self, val):
|
|
73
|
+
if val <= 0.0:
|
|
74
|
+
return np.nan
|
|
75
|
+
return (self.alpha - 1)/val - 2*self.beta*val + self.gamma
|
|
76
|
+
|
|
77
|
+
def _gradient(self, val, *args, **kwargs):
|
|
78
|
+
if hasattr(self.alpha, '__iter__'):
|
|
79
|
+
return np.array([self._gradient_scalar(v) for v in val])
|
|
80
|
+
else:
|
|
81
|
+
return np.array([self.dim*[self._gradient_scalar(v)] for v in val])
|
|
82
|
+
|
|
83
|
+
def _MHN_sample_gamma_proposal(self, alpha, beta, gamma, rng, delta=None):
|
|
84
|
+
"""
|
|
85
|
+
Sample from a modified half-normal distribution using a Gamma distribution proposal.
|
|
86
|
+
"""
|
|
87
|
+
if delta is None:
|
|
88
|
+
delta = beta + (gamma*gamma - gamma*np.sqrt(gamma*gamma + 8*beta*alpha))/(4*alpha)
|
|
89
|
+
|
|
90
|
+
while True:
|
|
91
|
+
T = rng.gamma(alpha/2, 1.0/delta)
|
|
92
|
+
X = np.sqrt(T)
|
|
93
|
+
U = rng.uniform()
|
|
94
|
+
if X > 0 and np.log(U) < -(beta-delta)*T + gamma*X - gamma*gamma/(4*(beta-delta)):
|
|
95
|
+
return X
|
|
96
|
+
|
|
97
|
+
def _MHN_sample_normal_proposal(self, alpha, beta, gamma, mu, rng):
|
|
98
|
+
"""
|
|
99
|
+
Sample from a modified half-normal distribution using a Normal/Gaussian distribution proposal.
|
|
100
|
+
"""
|
|
101
|
+
if mu is None:
|
|
102
|
+
mu = (gamma + np.sqrt(gamma*gamma + 8*beta*(alpha - 1)))/(4*beta)
|
|
103
|
+
|
|
104
|
+
while True:
|
|
105
|
+
X = rng.normal(mu, np.sqrt(0.5/beta))
|
|
106
|
+
U = rng.uniform()
|
|
107
|
+
if X > 0 and np.log(U) < (alpha-1)*np.log(X) - np.log(mu) + (2*beta*mu-gamma)*(mu-X):
|
|
108
|
+
return X
|
|
109
|
+
|
|
110
|
+
def _MHN_sample_positive_gamma_1(self, alpha, beta, gamma, rng):
|
|
111
|
+
"""
|
|
112
|
+
Sample from a modified half-normal distribution, assuming alpha is greater than one and gamma is positive.
|
|
113
|
+
"""
|
|
114
|
+
if gamma <= 0.0:
|
|
115
|
+
raise ValueError("gamma needs to be positive")
|
|
116
|
+
|
|
117
|
+
if alpha <= 1.0:
|
|
118
|
+
raise ValueError("alpha needs to be greater than 1.0")
|
|
119
|
+
|
|
120
|
+
# Decide whether to use Normal or sqrt(Gamma) proposals for acceptance-rejectance scheme
|
|
121
|
+
mu = (gamma + np.sqrt(gamma*gamma + 8*beta*(alpha - 1)))/(4*beta)
|
|
122
|
+
K1 = 2*np.sqrt(np.pi)
|
|
123
|
+
K1 *= np.power((np.sqrt(beta)*(alpha-1))/(2*beta*mu-gamma), alpha - 1)
|
|
124
|
+
K1 *= np.exp(-(alpha-1)+beta*mu*mu)
|
|
125
|
+
|
|
126
|
+
delta = beta + (gamma*gamma - gamma*np.sqrt(gamma*gamma + 8*beta*alpha))/(4*alpha)
|
|
127
|
+
K2 = np.power(beta/delta, 0.5*alpha)
|
|
128
|
+
K2 *= special.gamma(alpha/2.0)
|
|
129
|
+
K2 *= np.exp(gamma*gamma/(4*(beta-delta)))
|
|
130
|
+
|
|
131
|
+
if K2 > K1: # Use normal proposal
|
|
132
|
+
return self._MHN_sample_normal_proposal(alpha, beta, gamma, mu, rng)
|
|
133
|
+
else: # Use sqrt(gamma) proposal
|
|
134
|
+
return self._MHN_sample_gamma_proposal(alpha, beta, gamma, rng, delta)
|
|
135
|
+
|
|
136
|
+
def _MHN_sample_negative_gamma(self, alpha, beta, gamma, rng, m=None):
|
|
137
|
+
"""
|
|
138
|
+
Sample from a modified half-normal distribution, assuming gamma is negative.
|
|
139
|
+
The argument 'm' is the matching point, see Algorithm 3 from [1] for details.
|
|
140
|
+
"""
|
|
141
|
+
if gamma > 0.0:
|
|
142
|
+
raise ValueError("gamma needs to be negative")
|
|
143
|
+
|
|
144
|
+
if m is None:
|
|
145
|
+
if alpha <= 1.0:
|
|
146
|
+
m = 1.0
|
|
147
|
+
else:
|
|
148
|
+
m = "mode"
|
|
149
|
+
|
|
150
|
+
# The acceptance rate of this choice is at least 0.5*sqrt(2) approx 70.7 percent, according to Theorem 4 from [1].
|
|
151
|
+
if isinstance(m, str) and m.lower() == "mode":
|
|
152
|
+
m = (gamma + np.sqrt(gamma*gamma + 8*beta*alpha))/(4*beta)
|
|
153
|
+
|
|
154
|
+
while True:
|
|
155
|
+
val1 = (beta*m-gamma)/(2*beta*m-gamma)
|
|
156
|
+
val2 = m*(beta*m-gamma)
|
|
157
|
+
T = rng.gamma(alpha*val1, 1.0/val2)
|
|
158
|
+
X = m*np.power(T,val1)
|
|
159
|
+
U = rng.uniform()
|
|
160
|
+
if np.log(U) < val2*T-beta*X*X+gamma*X:
|
|
161
|
+
return X
|
|
162
|
+
|
|
163
|
+
def _MHN_sample(self, alpha, beta, gamma, m=None, rng=None):
|
|
164
|
+
"""
|
|
165
|
+
Sample from a modified half-normal distribution using an algorithm from [1].
|
|
166
|
+
"""
|
|
167
|
+
if rng == None:
|
|
168
|
+
rng = np.random
|
|
169
|
+
|
|
170
|
+
if gamma <= 0.0:
|
|
171
|
+
return self._MHN_sample_negative_gamma(alpha, beta, gamma, m=m, rng=rng)
|
|
172
|
+
|
|
173
|
+
if alpha > 1:
|
|
174
|
+
return self._MHN_sample_positive_gamma_1(alpha, beta, gamma, rng=rng)
|
|
175
|
+
|
|
176
|
+
return self._MHN_sample_gamma_proposal(alpha, beta, gamma, rng=rng)
|
|
177
|
+
|
|
178
|
+
def _sample(self, N, rng=None):
|
|
179
|
+
if hasattr(self.alpha, '__getitem__'):
|
|
180
|
+
return np.array([self._MHN_sample(self.alpha[i], self.beta[i], self.gamma[i], rng=rng) for i in range(N)])
|
|
181
|
+
else:
|
|
182
|
+
return np.array([self._MHN_sample(self.alpha, self.beta, self.gamma, rng=rng) for i in range(N)])
|
|
183
|
+
|
|
184
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|