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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: CUQIpy
3
- Version: 1.0.0.post0.dev337
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=bop6laBkqMl8sVGPBD_LKMsjPG6Dv1XBkb3SMN2t8KY,510
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=f85DzOHPvGec9nr_AIfp_THSuC4WN8ZUJMSLZrKClG8,615
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.dev337.dist-info/LICENSE,sha256=kJWRPrtRoQoZGXyyvu50Uc91X6_0XRaVfT0YZssicys,10799
85
- CUQIpy-1.0.0.post0.dev337.dist-info/METADATA,sha256=d44JgxdIjhKP_Z33mhZ3vll3JbJrLsxAIIIpFh6N6WU,18393
86
- CUQIpy-1.0.0.post0.dev337.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
87
- CUQIpy-1.0.0.post0.dev337.dist-info/top_level.txt,sha256=AgmgMc6TKfPPqbjV0kvAoCBN334i_Lwwojc7HE3ZwD0,5
88
- CUQIpy-1.0.0.post0.dev337.dist-info/RECORD,,
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-11T10:55:36+0200",
11
+ "date": "2024-06-19T09:05:37+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "b3c53f2ac21c807186dc35053421615c1cb3f205",
15
- "version": "1.0.0.post0.dev337"
14
+ "full-revisionid": "1e1deddbe7925206fdaef6a2d9bf8ce9e33ad73d",
15
+ "version": "1.0.0.post0.dev352"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -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
+