CUQIpy 1.2.0.post0.dev446__tar.gz → 1.2.0.post0.dev501__tar.gz
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.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/CUQIpy.egg-info/PKG-INFO +1 -1
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/PKG-INFO +1 -1
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/_version.py +3 -3
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_modifiedhalfnormal.py +13 -16
- cuqipy-1.2.0.post0.dev501/cuqi/experimental/__init__.py +4 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/algebra/_randomvariable.py +26 -0
- cuqipy-1.2.0.post0.dev501/cuqi/experimental/mcmc/_conjugate.py +335 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_conjugate_approx.py +11 -16
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/implicitprior/_regularizedGMRF.py +2 -2
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/implicitprior/_regularizedGaussian.py +7 -23
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/implicitprior/_regularizedUnboundedUniform.py +2 -2
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/problem/_problem.py +3 -3
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/solver/_solver.py +1 -1
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/utilities/__init__.py +4 -1
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/utilities/_utilities.py +99 -1
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_distribution.py +4 -5
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_distributions_shape.py +1 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_utilities.py +34 -2
- cuqipy-1.2.0.post0.dev446/cuqi/experimental/__init__.py +0 -4
- cuqipy-1.2.0.post0.dev446/cuqi/experimental/mcmc/_conjugate.py +0 -197
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/CUQIpy.egg-info/SOURCES.txt +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/CUQIpy.egg-info/dependency_links.txt +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/CUQIpy.egg-info/requires.txt +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/CUQIpy.egg-info/top_level.txt +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/LICENSE +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/README.md +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/_messages.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/array/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/array/_array.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/config.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/data/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/data/_data.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/data/astronaut.npz +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/data/camera.npz +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/data/cat.npz +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/data/cookie.png +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/data/satellite.mat +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/density/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/density/_density.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/diagnostics.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_beta.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_cauchy.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_cmrf.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_custom.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_gamma.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_gaussian.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_gmrf.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_inverse_gamma.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_joint_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_laplace.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_lmrf.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_lognormal.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_normal.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_posterior.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_smoothed_laplace.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_truncated_normal.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_uniform.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/algebra/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/algebra/_ast.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/algebra/_orderedset.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/geometry/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/geometry/_productgeometry.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_cwmh.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_direct.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_gibbs.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_hmc.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_langevin_algorithm.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_laplace_approximation.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_mh.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_pcn.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_rto.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_sampler.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_utilities.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/geometry/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/geometry/_geometry.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/implicitprior/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/implicitprior/_restorator.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/likelihood/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/likelihood/_likelihood.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/model/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/model/_model.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/operator/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/operator/_operator.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/pde/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/pde/_pde.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/problem/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_conjugate.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_conjugate_approx.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_cwmh.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_gibbs.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_hmc.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_langevin_algorithm.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_laplace_approximation.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_mh.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_pcn.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_rto.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/sampler/_sampler.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/samples/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/samples/_samples.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/solver/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/testproblem/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/testproblem/_testproblem.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/utilities/_get_python_variable_name.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/pyproject.toml +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/requirements.txt +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/setup.cfg +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/setup.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_MRFs.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_abstract_distribution_density.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_bayesian_inversion.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_density.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_geometry.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_implicit_priors.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_joint_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_likelihood.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_model.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_pde.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_posterior.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_problem.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_sampler.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_samples.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_solver.py +0 -0
- {cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/tests/test_testproblem.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: CUQIpy
|
|
3
|
-
Version: 1.2.0.post0.
|
|
3
|
+
Version: 1.2.0.post0.dev501
|
|
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
|
Metadata-Version: 2.2
|
|
2
2
|
Name: CUQIpy
|
|
3
|
-
Version: 1.2.0.post0.
|
|
3
|
+
Version: 1.2.0.post0.dev501
|
|
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
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-01-
|
|
11
|
+
"date": "2025-01-30T19:07:50+0100",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.2.0.post0.
|
|
14
|
+
"full-revisionid": "ead079a625cad175f6a0159d46c90d7cdf80bc0f",
|
|
15
|
+
"version": "1.2.0.post0.dev501"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
{cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/distribution/_modifiedhalfnormal.py
RENAMED
|
@@ -24,13 +24,13 @@ class ModifiedHalfNormal(Distribution):
|
|
|
24
24
|
|
|
25
25
|
Parameters
|
|
26
26
|
----------
|
|
27
|
-
alpha : float
|
|
27
|
+
alpha : float or array_like
|
|
28
28
|
The polynomial exponent parameter :math:`\\alpha` of the MHN distribution. Must be positive.
|
|
29
29
|
|
|
30
|
-
beta : float
|
|
30
|
+
beta : float or array_like
|
|
31
31
|
The quadratic exponential parameter :math:`\\beta` of the MHN distribution. Must be positive.
|
|
32
32
|
|
|
33
|
-
gamma : float
|
|
33
|
+
gamma : float or array_like
|
|
34
34
|
The linear exponential parameter :math:`\\gamma` of the MHN distribution.
|
|
35
35
|
|
|
36
36
|
"""
|
|
@@ -38,9 +38,9 @@ class ModifiedHalfNormal(Distribution):
|
|
|
38
38
|
# Init from abstract distribution class
|
|
39
39
|
super().__init__(is_symmetric=is_symmetric, **kwargs)
|
|
40
40
|
|
|
41
|
-
self.
|
|
42
|
-
self.
|
|
43
|
-
self.
|
|
41
|
+
self.alpha = alpha
|
|
42
|
+
self.beta = beta
|
|
43
|
+
self.gamma = gamma
|
|
44
44
|
|
|
45
45
|
@property
|
|
46
46
|
def alpha(self):
|
|
@@ -48,13 +48,13 @@ class ModifiedHalfNormal(Distribution):
|
|
|
48
48
|
return self._alpha
|
|
49
49
|
|
|
50
50
|
@alpha.setter
|
|
51
|
-
def
|
|
52
|
-
self.
|
|
51
|
+
def alpha(self, value):
|
|
52
|
+
self._alpha = force_ndarray(value, flatten=True)
|
|
53
53
|
|
|
54
54
|
@property
|
|
55
55
|
def beta(self):
|
|
56
56
|
""" The quadratic exponential parameter of the MHN distribution. Must be positive. """
|
|
57
|
-
return self.
|
|
57
|
+
return self._beta
|
|
58
58
|
|
|
59
59
|
@beta.setter
|
|
60
60
|
def beta(self, value):
|
|
@@ -63,7 +63,7 @@ class ModifiedHalfNormal(Distribution):
|
|
|
63
63
|
@property
|
|
64
64
|
def gamma(self):
|
|
65
65
|
""" The linear exponential parameter of the MHN distribution. """
|
|
66
|
-
return self.
|
|
66
|
+
return self._gamma
|
|
67
67
|
|
|
68
68
|
@gamma.setter
|
|
69
69
|
def gamma(self, value):
|
|
@@ -78,11 +78,8 @@ class ModifiedHalfNormal(Distribution):
|
|
|
78
78
|
return (self.alpha - 1)/val - 2*self.beta*val + self.gamma
|
|
79
79
|
|
|
80
80
|
def _gradient(self, val, *args, **kwargs):
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
else:
|
|
84
|
-
return np.array([self.dim*[self._gradient_scalar(v)] for v in val])
|
|
85
|
-
|
|
81
|
+
return np.array([self._gradient_scalar(v) for v in val])
|
|
82
|
+
|
|
86
83
|
def _MHN_sample_gamma_proposal(self, alpha, beta, gamma, rng, delta=None):
|
|
87
84
|
"""
|
|
88
85
|
Sample from a modified half-normal distribution using a Gamma distribution proposal.
|
|
@@ -180,7 +177,7 @@ class ModifiedHalfNormal(Distribution):
|
|
|
180
177
|
|
|
181
178
|
def _sample(self, N, rng=None):
|
|
182
179
|
if hasattr(self.alpha, '__getitem__'):
|
|
183
|
-
return np.array([self._MHN_sample(self.alpha[i], self.beta[i], self.gamma[i], rng=rng) for i in range(N)])
|
|
180
|
+
return np.array([[self._MHN_sample(self.alpha[i], self.beta[i], self.gamma[i], rng=rng) for i in range(len(self.alpha))] for _ in range(N)])
|
|
184
181
|
else:
|
|
185
182
|
return np.array([self._MHN_sample(self.alpha, self.beta, self.gamma, rng=rng) for i in range(N)])
|
|
186
183
|
|
{cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/algebra/_randomvariable.py
RENAMED
|
@@ -6,6 +6,8 @@ import operator
|
|
|
6
6
|
import cuqi
|
|
7
7
|
from cuqi.distribution import Distribution
|
|
8
8
|
from copy import copy, deepcopy
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
class RandomVariable:
|
|
11
13
|
""" Random variable defined by a distribution with the option to apply algebraic operations on it.
|
|
@@ -165,6 +167,30 @@ class RandomVariable:
|
|
|
165
167
|
raise ValueError(f"Expected arguments {self.parameter_names}, got arguments {kwargs}")
|
|
166
168
|
|
|
167
169
|
return self.tree(**kwargs)
|
|
170
|
+
|
|
171
|
+
def sample(self, N=1):
|
|
172
|
+
""" Sample from the random variable.
|
|
173
|
+
|
|
174
|
+
Parameters
|
|
175
|
+
----------
|
|
176
|
+
N : int, optional
|
|
177
|
+
Number of samples to draw. Default is 1.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
if self.is_cond:
|
|
181
|
+
raise NotImplementedError(
|
|
182
|
+
"Unable to directly sample from a random variable that has distributions with "
|
|
183
|
+
"conditioning variables. This is not implemented."
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if N == 1: return self(**{dist.name: dist.sample() for dist in self.distributions})
|
|
187
|
+
|
|
188
|
+
samples = np.array([
|
|
189
|
+
self(**{dist.name: dist.sample() for dist in self.distributions})
|
|
190
|
+
for _ in range(N)
|
|
191
|
+
]).reshape(-1, N) # Ensure correct shape (dim, N)
|
|
192
|
+
|
|
193
|
+
return cuqi.samples.Samples(samples)
|
|
168
194
|
|
|
169
195
|
@property
|
|
170
196
|
def tree(self):
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
import math
|
|
4
|
+
from cuqi.experimental.mcmc import Sampler
|
|
5
|
+
from cuqi.distribution import Posterior, Gaussian, Gamma, GMRF, ModifiedHalfNormal
|
|
6
|
+
from cuqi.implicitprior import RegularizedGaussian, RegularizedGMRF, RegularizedUnboundedUniform
|
|
7
|
+
from cuqi.utilities import get_non_default_args, count_nonzero, count_constant_components_1D, count_constant_components_2D
|
|
8
|
+
from cuqi.geometry import Continuous1D, Continuous2D, Image2D
|
|
9
|
+
|
|
10
|
+
class Conjugate(Sampler):
|
|
11
|
+
""" Conjugate sampler
|
|
12
|
+
|
|
13
|
+
Sampler for sampling a posterior distribution which is a so-called "conjugate" distribution, i.e., where the likelihood and prior are conjugate to each other - denoted as a conjugate pair.
|
|
14
|
+
|
|
15
|
+
Currently supported conjugate pairs are:
|
|
16
|
+
- (Gaussian, Gamma) where Gamma is defined on the precision parameter of the Gaussian
|
|
17
|
+
- (GMRF, Gamma) where Gamma is defined on the precision parameter of the GMRF
|
|
18
|
+
- (RegularizedGaussian, Gamma) with preset constraints only and Gamma is defined on the precision parameter of the RegularizedGaussian
|
|
19
|
+
- (RegularizedGMRF, Gamma) with preset constraints only and Gamma is defined on the precision parameter of the RegularizedGMRF
|
|
20
|
+
- (RegularizedGaussian, ModifiedHalfNormal) with preset constraints and regularization only
|
|
21
|
+
- (RegularizedGMRF, ModifiedHalfNormal) with preset constraints and regularization only
|
|
22
|
+
|
|
23
|
+
Currently the Gamma and ModifiedHalfNormal distribution must be univariate.
|
|
24
|
+
|
|
25
|
+
A conjugate pair defines implicitly a so-called conjugate distribution which can be sampled from directly.
|
|
26
|
+
|
|
27
|
+
The conjugate parameter is the parameter that both the likelihood and prior PDF depend on.
|
|
28
|
+
|
|
29
|
+
For more information on conjugacy and conjugate distributions see https://en.wikipedia.org/wiki/Conjugate_prior.
|
|
30
|
+
|
|
31
|
+
For implicit regularized Gaussians and the corresponding conjugacy relations, see:
|
|
32
|
+
|
|
33
|
+
Section 3.3 from [1] Everink, Jasper M., Yiqiu Dong, and Martin S. Andersen. "Bayesian inference with projected densities." SIAM/ASA Journal on Uncertainty Quantification 11.3 (2023): 1025-1043.
|
|
34
|
+
Section 4 from [2] Everink, Jasper M., Yiqiu Dong, and Martin S. Andersen. "Sparse Bayesian inference with regularized Gaussian distributions." Inverse Problems 39.11 (2023): 115004.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def _initialize(self):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
@Sampler.target.setter # Overwrite the target setter to set the conjugate pair
|
|
42
|
+
def target(self, value):
|
|
43
|
+
""" Set the target density. Runs validation of the target. """
|
|
44
|
+
self._target = value
|
|
45
|
+
if self._target is not None:
|
|
46
|
+
self._set_conjugatepair()
|
|
47
|
+
self.validate_target()
|
|
48
|
+
|
|
49
|
+
def validate_target(self):
|
|
50
|
+
self._ensure_target_is_posterior()
|
|
51
|
+
self._conjugatepair.validate_target()
|
|
52
|
+
|
|
53
|
+
def step(self):
|
|
54
|
+
self.current_point = self._conjugatepair.sample()
|
|
55
|
+
return 1 # Returns acceptance rate of 1
|
|
56
|
+
|
|
57
|
+
def tune(self, skip_len, update_count):
|
|
58
|
+
pass # No tuning required for conjugate sampler
|
|
59
|
+
|
|
60
|
+
def _ensure_target_is_posterior(self):
|
|
61
|
+
""" Ensure that the target is a Posterior distribution. """
|
|
62
|
+
if not isinstance(self.target, Posterior):
|
|
63
|
+
raise TypeError("Conjugate sampler requires a target of type Posterior")
|
|
64
|
+
|
|
65
|
+
def _set_conjugatepair(self):
|
|
66
|
+
""" Set the conjugate pair based on the likelihood and prior. This requires target to be set. """
|
|
67
|
+
self._ensure_target_is_posterior()
|
|
68
|
+
if isinstance(self.target.likelihood.distribution, (Gaussian, GMRF)) and isinstance(self.target.prior, Gamma):
|
|
69
|
+
self._conjugatepair = _GaussianGammaPair(self.target)
|
|
70
|
+
elif isinstance(self.target.likelihood.distribution, RegularizedUnboundedUniform) and isinstance(self.target.prior, Gamma):
|
|
71
|
+
# Check RegularizedUnboundedUniform before RegularizedGaussian and RegularizedGMRF due to the first inheriting from the second.
|
|
72
|
+
self._conjugatepair = _RegularizedUnboundedUniformGammaPair(self.target)
|
|
73
|
+
elif isinstance(self.target.likelihood.distribution, (RegularizedGaussian, RegularizedGMRF)) and isinstance(self.target.prior, Gamma):
|
|
74
|
+
self._conjugatepair = _RegularizedGaussianGammaPair(self.target)
|
|
75
|
+
elif isinstance(self.target.likelihood.distribution, (RegularizedGaussian, RegularizedGMRF)) and isinstance(self.target.prior, ModifiedHalfNormal):
|
|
76
|
+
self._conjugatepair = _RegularizedGaussianModifiedHalfNormalPair(self.target)
|
|
77
|
+
else:
|
|
78
|
+
raise ValueError(f"Conjugacy is not defined for likelihood {type(self.target.likelihood.distribution)} and prior {type(self.target.prior)}, in CUQIpy")
|
|
79
|
+
|
|
80
|
+
def conjugate_distribution(self):
|
|
81
|
+
return self._conjugatepair.conjugate_distribution()
|
|
82
|
+
|
|
83
|
+
def __repr__(self):
|
|
84
|
+
msg = super().__repr__()
|
|
85
|
+
if hasattr(self, "_conjugatepair"):
|
|
86
|
+
msg += f"\n Conjugate pair:\n\t {type(self._conjugatepair).__name__.removeprefix('_')}"
|
|
87
|
+
return msg
|
|
88
|
+
|
|
89
|
+
class _ConjugatePair(ABC):
|
|
90
|
+
""" Abstract base class for conjugate pairs (likelihood, prior) used in the Conjugate sampler. """
|
|
91
|
+
|
|
92
|
+
def __init__(self, target):
|
|
93
|
+
self.target = target
|
|
94
|
+
|
|
95
|
+
@abstractmethod
|
|
96
|
+
def validate_target(self):
|
|
97
|
+
""" Validate the target distribution for the conjugate pair. """
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
@abstractmethod
|
|
101
|
+
def conjugate_distribution(self):
|
|
102
|
+
""" Returns the posterior distribution in the form of a CUQIpy distribution """
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
def sample(self):
|
|
106
|
+
""" Sample from the conjugate distribution. """
|
|
107
|
+
return self.conjugate_distribution().sample()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class _GaussianGammaPair(_ConjugatePair):
|
|
111
|
+
""" Implementation for the Gaussian-Gamma conjugate pair."""
|
|
112
|
+
|
|
113
|
+
def validate_target(self):
|
|
114
|
+
if self.target.prior.dim != 1:
|
|
115
|
+
raise ValueError("Gaussian-Gamma conjugacy only works with univariate Gamma prior")
|
|
116
|
+
|
|
117
|
+
key_value_pairs = _get_conjugate_parameter(self.target)
|
|
118
|
+
if len(key_value_pairs) != 1:
|
|
119
|
+
raise ValueError(f"Multiple references to conjugate parameter {self.target.prior.name} found in likelihood. Only one occurance is supported.")
|
|
120
|
+
for key, value in key_value_pairs:
|
|
121
|
+
if key == "cov":
|
|
122
|
+
if not _check_conjugate_parameter_is_scalar_linear_reciprocal(value):
|
|
123
|
+
raise ValueError("Gaussian-Gamma conjugate pair defined via covariance requires cov: lambda x : s/x for the conjugate parameter")
|
|
124
|
+
elif key == "prec":
|
|
125
|
+
if not _check_conjugate_parameter_is_scalar_linear(value):
|
|
126
|
+
raise ValueError("Gaussian-Gamma conjugate pair defined via precision requires prec: lambda x : s*x for the conjugate parameter")
|
|
127
|
+
else:
|
|
128
|
+
raise ValueError(f"RegularizedGaussian-ModifiedHalfNormal conjugacy does not support the conjugate parameter {self.target.prior.name} in the {key} attribute. Only cov and prec")
|
|
129
|
+
|
|
130
|
+
def conjugate_distribution(self):
|
|
131
|
+
# Extract variables
|
|
132
|
+
b = self.target.likelihood.data # mu
|
|
133
|
+
m = len(b) # n
|
|
134
|
+
Ax = self.target.likelihood.distribution.mean # x_i
|
|
135
|
+
L = self.target.likelihood.distribution(np.array([1])).sqrtprec # L
|
|
136
|
+
alpha = self.target.prior.shape # alpha
|
|
137
|
+
beta = self.target.prior.rate # beta
|
|
138
|
+
|
|
139
|
+
# Create Gamma distribution and sample
|
|
140
|
+
return Gamma(shape=m/2 + alpha, rate=.5 * np.linalg.norm(L @ (Ax - b))**2 + beta)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class _RegularizedGaussianGammaPair(_ConjugatePair):
|
|
144
|
+
"""Implementation for the Regularized Gaussian-Gamma conjugate pair using the conjugacy rules from [1], Section 3.3."""
|
|
145
|
+
|
|
146
|
+
def validate_target(self):
|
|
147
|
+
if self.target.prior.dim != 1:
|
|
148
|
+
raise ValueError("RegularizedGaussian-Gamma conjugacy only works with univariate ModifiedHalfNormal prior")
|
|
149
|
+
|
|
150
|
+
if self.target.likelihood.distribution.preset not in ["nonnegativity"]:
|
|
151
|
+
raise ValueError("RegularizedGaussian-Gamma conjugacy only works with implicit regularized Gaussian likelihood with nonnegativity constraints")
|
|
152
|
+
|
|
153
|
+
key_value_pairs = _get_conjugate_parameter(self.target)
|
|
154
|
+
if len(key_value_pairs) != 1:
|
|
155
|
+
raise ValueError(f"Multiple references to conjugate parameter {self.target.prior.name} found in likelihood. Only one occurance is supported.")
|
|
156
|
+
for key, value in key_value_pairs:
|
|
157
|
+
if key == "cov":
|
|
158
|
+
if not _check_conjugate_parameter_is_scalar_linear_reciprocal(value):
|
|
159
|
+
raise ValueError("Regularized Gaussian-Gamma conjugacy defined via covariance requires cov: lambda x : s/x for the conjugate parameter")
|
|
160
|
+
elif key == "prec":
|
|
161
|
+
if not _check_conjugate_parameter_is_scalar_linear(value):
|
|
162
|
+
raise ValueError("Regularized Gaussian-Gamma conjugacy defined via precision requires prec: lambda x : s*x for the conjugate parameter")
|
|
163
|
+
else:
|
|
164
|
+
raise ValueError(f"RegularizedGaussian-ModifiedHalfNormal conjugacy does not support the conjugate parameter {self.target.prior.name} in the {key} attribute. Only cov and prec")
|
|
165
|
+
|
|
166
|
+
def conjugate_distribution(self):
|
|
167
|
+
# Extract variables
|
|
168
|
+
b = self.target.likelihood.data # mu
|
|
169
|
+
m = np.count_nonzero(b) # n
|
|
170
|
+
Ax = self.target.likelihood.distribution.mean # x_i
|
|
171
|
+
L = self.target.likelihood.distribution(np.array([1])).sqrtprec # L
|
|
172
|
+
alpha = self.target.prior.shape # alpha
|
|
173
|
+
beta = self.target.prior.rate # beta
|
|
174
|
+
|
|
175
|
+
# Create Gamma distribution and sample
|
|
176
|
+
return Gamma(shape=m/2 + alpha, rate=.5 * np.linalg.norm(L @ (Ax - b))**2 + beta)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class _RegularizedUnboundedUniformGammaPair(_ConjugatePair):
|
|
180
|
+
"""Implementation for the RegularizedUnboundedUniform-ModifiedHalfNormal conjugate pair using the conjugacy rules from [2], Section 4."""
|
|
181
|
+
|
|
182
|
+
def validate_target(self):
|
|
183
|
+
if self.target.prior.dim != 1:
|
|
184
|
+
raise ValueError("RegularizedUnboundedUniform-Gamma conjugacy only works with univariate Gamma prior")
|
|
185
|
+
|
|
186
|
+
if self.target.likelihood.distribution.preset not in ["l1", "tv"]:
|
|
187
|
+
raise ValueError("RegularizedUnboundedUniform-Gamma conjugacy only works with implicit regularized Gaussian likelihood with l1 or tv regularization")
|
|
188
|
+
|
|
189
|
+
key_value_pairs = _get_conjugate_parameter(self.target)
|
|
190
|
+
if len(key_value_pairs) != 1:
|
|
191
|
+
raise ValueError(f"Multiple references to conjugate parameter {self.target.prior.name} found in likelihood. Only one occurance is supported.")
|
|
192
|
+
for key, value in key_value_pairs:
|
|
193
|
+
if key == "strength":
|
|
194
|
+
if not _check_conjugate_parameter_is_scalar_linear(value):
|
|
195
|
+
raise ValueError("RegularizedUnboundedUniform-Gamma conjugacy defined via strength requires strength: lambda x : s*x for the conjugate parameter")
|
|
196
|
+
else:
|
|
197
|
+
raise ValueError(f"RegularizedUnboundedUniform-Gamma conjugacy does not support the conjugate parameter {self.target.prior.name} in the {key} attribute. Only strength is supported")
|
|
198
|
+
|
|
199
|
+
def conjugate_distribution(self):
|
|
200
|
+
# Extract prior variables
|
|
201
|
+
alpha = self.target.prior.shape
|
|
202
|
+
beta = self.target.prior.rate
|
|
203
|
+
|
|
204
|
+
# Compute likelihood quantities
|
|
205
|
+
x = self.target.likelihood.data
|
|
206
|
+
if self.target.likelihood.distribution.preset == "l1":
|
|
207
|
+
m = count_nonzero(x)
|
|
208
|
+
elif self.target.likelihood.distribution.preset == "tv" and isinstance(self.target.likelihood.distribution.geometry, Continuous1D):
|
|
209
|
+
m = count_constant_components_1D(x)
|
|
210
|
+
elif self.target.likelihood.distribution.preset == "tv" and isinstance(self.target.likelihood.distribution.geometry, (Continuous2D, Image2D)):
|
|
211
|
+
m = count_constant_components_2D(self.target.likelihood.distribution.geometry.par2fun(x))
|
|
212
|
+
|
|
213
|
+
reg_op = self.target.likelihood.distribution._regularization_oper
|
|
214
|
+
reg_strength = self.target.likelihood.distribution(np.array([1])).strength
|
|
215
|
+
fx = reg_strength*np.linalg.norm(reg_op@x, ord = 1)
|
|
216
|
+
|
|
217
|
+
# Create Gamma distribution
|
|
218
|
+
return Gamma(shape=m/2 + alpha, rate=fx + beta)
|
|
219
|
+
|
|
220
|
+
class _RegularizedGaussianModifiedHalfNormalPair(_ConjugatePair):
|
|
221
|
+
"""Implementation for the Regularized Gaussian-ModifiedHalfNormal conjugate pair using the conjugacy rules from [2], Section 4."""
|
|
222
|
+
|
|
223
|
+
def validate_target(self):
|
|
224
|
+
if self.target.prior.dim != 1:
|
|
225
|
+
raise ValueError("RegularizedGaussian-ModifiedHalfNormal conjugacy only works with univariate ModifiedHalfNormal prior")
|
|
226
|
+
|
|
227
|
+
if self.target.likelihood.distribution.preset not in ["l1", "tv"]:
|
|
228
|
+
raise ValueError("RegularizedGaussian-ModifiedHalfNormal conjugacy only works with implicit regularized Gaussian likelihood with l1 or tv regularization")
|
|
229
|
+
|
|
230
|
+
key_value_pairs = _get_conjugate_parameter(self.target)
|
|
231
|
+
if len(key_value_pairs) != 2:
|
|
232
|
+
raise ValueError(f"Incorrect number of references to conjugate parameter {self.target.prior.name} found in likelihood. Found {len(key_value_pairs)} times, but needs to occur in prec or cov, and in strength")
|
|
233
|
+
for key, value in key_value_pairs:
|
|
234
|
+
if key == "strength":
|
|
235
|
+
if not _check_conjugate_parameter_is_scalar_linear(value):
|
|
236
|
+
raise ValueError("RegularizedGaussian-ModifiedHalfNormal conjugacy defined via strength requires strength: lambda x : s*x for the conjugate parameter")
|
|
237
|
+
elif key == "prec":
|
|
238
|
+
if not _check_conjugate_parameter_is_scalar_quadratic(value):
|
|
239
|
+
raise ValueError("RegularizedGaussian-ModifiedHalfNormal conjugacy defined via precision requires prec: lambda x : s*x for the conjugate parameter")
|
|
240
|
+
elif key == "cov":
|
|
241
|
+
if not _check_conjugate_parameter_is_scalar_quadratic_reciprocal(value):
|
|
242
|
+
raise ValueError("RegularizedGaussian-ModifiedHalfNormal conjugacy defined via covariance requires cov: lambda x : s/x for the conjugate parameter")
|
|
243
|
+
else:
|
|
244
|
+
raise ValueError(f"RegularizedGaussian-ModifiedHalfNormal conjugacy does not support the conjugate parameter {self.target.prior.name} in the {key} attribute. Only cov, prec and strength are supported")
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def conjugate_distribution(self):
|
|
248
|
+
# Extract prior variables
|
|
249
|
+
alpha = self.target.prior.alpha
|
|
250
|
+
beta = self.target.prior.beta
|
|
251
|
+
gamma = self.target.prior.gamma
|
|
252
|
+
|
|
253
|
+
# Compute likelihood variables
|
|
254
|
+
x = self.target.likelihood.data
|
|
255
|
+
mu = self.target.likelihood.distribution.mean
|
|
256
|
+
L = self.target.likelihood.distribution(np.array([1])).sqrtprec
|
|
257
|
+
|
|
258
|
+
if self.target.likelihood.distribution.preset == "l1":
|
|
259
|
+
m = count_nonzero(x)
|
|
260
|
+
elif self.target.likelihood.distribution.preset == "tv" and isinstance(self.target.likelihood.distribution.geometry, Continuous1D):
|
|
261
|
+
m = count_constant_components_1D(x)
|
|
262
|
+
elif self.target.likelihood.distribution.preset == "tv" and isinstance(self.target.likelihood.distribution.geometry, (Continuous2D, Image2D)):
|
|
263
|
+
m = count_constant_components_2D(self.target.likelihood.distribution.geometry.par2fun(x))
|
|
264
|
+
|
|
265
|
+
reg_op = self.target.likelihood.distribution._regularization_oper
|
|
266
|
+
reg_strength = self.target.likelihood.distribution(np.array([1])).strength
|
|
267
|
+
fx = reg_strength*np.linalg.norm(reg_op@x, ord = 1)
|
|
268
|
+
|
|
269
|
+
# Compute parameters of conjugate distribution
|
|
270
|
+
conj_alpha = m + alpha
|
|
271
|
+
conj_beta = 0.5*np.linalg.norm(L @ (mu - x))**2 + beta
|
|
272
|
+
conj_gamma = -fx + gamma
|
|
273
|
+
|
|
274
|
+
# Create conjugate distribution
|
|
275
|
+
return ModifiedHalfNormal(conj_alpha, conj_beta, conj_gamma)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _get_conjugate_parameter(target):
|
|
279
|
+
"""Extract the conjugate parameter name (e.g. d), and returns the mutable variable that is defined by the conjugate parameter, e.g. cov and its value e.g. lambda d:1/d"""
|
|
280
|
+
par_name = target.prior.name
|
|
281
|
+
mutable_likelihood_vars = target.likelihood.distribution.get_mutable_variables()
|
|
282
|
+
|
|
283
|
+
found_parameter_pairs = []
|
|
284
|
+
|
|
285
|
+
for var_key in mutable_likelihood_vars:
|
|
286
|
+
attr = getattr(target.likelihood.distribution, var_key)
|
|
287
|
+
if callable(attr) and par_name in get_non_default_args(attr):
|
|
288
|
+
found_parameter_pairs.append((var_key, attr))
|
|
289
|
+
if len(found_parameter_pairs) == 0:
|
|
290
|
+
raise ValueError(f"Unable to find conjugate parameter {par_name} in likelihood function for conjugate sampler with target {target}")
|
|
291
|
+
return found_parameter_pairs
|
|
292
|
+
|
|
293
|
+
def _check_conjugate_parameter_is_scalar_identity(f):
|
|
294
|
+
"""Tests whether a function (scalar to scalar) is the identity (lambda x: x)."""
|
|
295
|
+
test_values = [1.0, 10.0, 100.0]
|
|
296
|
+
return all(np.allclose(f(x), x) for x in test_values)
|
|
297
|
+
|
|
298
|
+
def _check_conjugate_parameter_is_scalar_reciprocal(f):
|
|
299
|
+
"""Tests whether a function (scalar to scalar) is the reciprocal (lambda x : 1.0/x)."""
|
|
300
|
+
return all(math.isclose(f(x), 1.0 / x) for x in [1.0, 10.0, 100.0])
|
|
301
|
+
|
|
302
|
+
def _check_conjugate_parameter_is_scalar_linear(f):
|
|
303
|
+
"""
|
|
304
|
+
Tests whether a function (scalar to scalar) is linear (lambda x: s*x for some s).
|
|
305
|
+
The tests checks whether the function is zero and some finite differences are constant.
|
|
306
|
+
"""
|
|
307
|
+
test_values = [1.0, 10.0, 100.0]
|
|
308
|
+
h = 1e-2
|
|
309
|
+
finite_diffs = [(f(x + h*x)-f(x))/(h*x) for x in test_values]
|
|
310
|
+
return np.isclose(f(0.0), 0.0) and all(np.allclose(c, finite_diffs[0]) for c in finite_diffs[1:])
|
|
311
|
+
|
|
312
|
+
def _check_conjugate_parameter_is_scalar_linear_reciprocal(f):
|
|
313
|
+
"""
|
|
314
|
+
Tests whether a function (scalar to scalar) is a constant times the inverse of the input (lambda x: s/x for some s).
|
|
315
|
+
The tests checks whether the the reciprocal of the function has constant finite differences.
|
|
316
|
+
"""
|
|
317
|
+
g = lambda x : 1.0/f(x)
|
|
318
|
+
test_values = [1.0, 10.0, 100.0]
|
|
319
|
+
h = 1e-2
|
|
320
|
+
finite_diffs = [(g(x + h*x)-g(x))/(h*x) for x in test_values]
|
|
321
|
+
return all(np.allclose(c, finite_diffs[0]) for c in finite_diffs[1:])
|
|
322
|
+
|
|
323
|
+
def _check_conjugate_parameter_is_scalar_quadratic(f):
|
|
324
|
+
"""
|
|
325
|
+
Tests whether a function (scalar to scalar) is linear (lambda x: s*x**2 for some s).
|
|
326
|
+
The tests checks whether the function divided by the parameter is linear
|
|
327
|
+
"""
|
|
328
|
+
return _check_conjugate_parameter_is_scalar_linear(lambda x: f(x)/x if x != 0.0 else f(0.0))
|
|
329
|
+
|
|
330
|
+
def _check_conjugate_parameter_is_scalar_quadratic_reciprocal(f):
|
|
331
|
+
"""
|
|
332
|
+
Tests whether a function (scalar to scalar) is linear (lambda x: s*x**-2 for some s).
|
|
333
|
+
The tests checks whether the function divided by the parameter is the reciprical of a linear function.
|
|
334
|
+
"""
|
|
335
|
+
return _check_conjugate_parameter_is_scalar_linear_reciprocal(lambda x: f(x)/x)
|
{cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/experimental/mcmc/_conjugate_approx.py
RENAMED
|
@@ -33,26 +33,23 @@ class _LMRFGammaPair(_ConjugatePair):
|
|
|
33
33
|
""" Implementation of the conjugate pair (LMRF, Gamma) """
|
|
34
34
|
|
|
35
35
|
def validate_target(self):
|
|
36
|
-
if not isinstance(self.target.likelihood.distribution, LMRF):
|
|
37
|
-
raise ValueError("Approximate conjugate sampler only works with LMRF likelihood function")
|
|
38
|
-
|
|
39
|
-
if not isinstance(self.target.prior, Gamma):
|
|
40
|
-
raise ValueError("Approximate conjugate sampler with LMRF likelihood only works with Gamma prior")
|
|
41
|
-
|
|
42
36
|
if not self.target.prior.dim == 1:
|
|
43
37
|
raise ValueError("Approximate conjugate sampler only works with univariate Gamma prior")
|
|
44
38
|
|
|
45
39
|
if np.sum(self.target.likelihood.distribution.location) != 0:
|
|
46
40
|
raise ValueError("Approximate conjugate sampler only works with zero mean LMRF likelihood")
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
if
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
key_value_pairs = _get_conjugate_parameter(self.target)
|
|
43
|
+
if len(key_value_pairs) != 1:
|
|
44
|
+
raise ValueError(f"Multiple references to conjugate parameter {self.target.prior.name} found in likelihood. Only one occurance is supported.")
|
|
45
|
+
for key, value in key_value_pairs:
|
|
46
|
+
if key == "scale":
|
|
47
|
+
if not _check_conjugate_parameter_is_scalar_reciprocal(value):
|
|
48
|
+
raise ValueError("Approximate conjugate sampler only works with Gamma prior on the inverse of the scale parameter of the LMRF likelihood")
|
|
49
|
+
else:
|
|
50
|
+
raise ValueError(f"No approximate conjugacy defined for likelihood {type(self.target.likelihood.distribution)} and prior {type(self.target.prior)}, in CUQIpy")
|
|
54
51
|
|
|
55
|
-
def
|
|
52
|
+
def conjugate_distribution(self):
|
|
56
53
|
# Extract variables
|
|
57
54
|
# Here we approximate the LMRF with a Gaussian
|
|
58
55
|
|
|
@@ -76,6 +73,4 @@ class _LMRFGammaPair(_ConjugatePair):
|
|
|
76
73
|
beta = self.target.prior.rate #beta
|
|
77
74
|
|
|
78
75
|
# Create Gamma distribution and sample
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return dist.sample()
|
|
76
|
+
return Gamma(shape=d+alpha, rate=np.linalg.norm(Lx)**2+beta)
|
{cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/implicitprior/_regularizedGMRF.py
RENAMED
|
@@ -50,9 +50,9 @@ class RegularizedGMRF(RegularizedGaussian):
|
|
|
50
50
|
|
|
51
51
|
regularization : string or None
|
|
52
52
|
Preset regularization. Can be set to "l1". Required for use in Gibbs in future update.
|
|
53
|
-
For "l1", the following additional parameters can be passed:
|
|
53
|
+
For "l1" or "tv", the following additional parameters can be passed:
|
|
54
54
|
strength : scalar
|
|
55
|
-
Regularization parameter, i.e., strength*||
|
|
55
|
+
Regularization parameter, i.e., strength*||Lx||_1, defaults to one
|
|
56
56
|
|
|
57
57
|
"""
|
|
58
58
|
def __init__(self, mean=None, prec=None, bc_type='zero', order=1, proximal = None, projector = None, constraint = None, regularization = None, **kwargs):
|
{cuqipy-1.2.0.post0.dev446 → cuqipy-1.2.0.post0.dev501}/cuqi/implicitprior/_regularizedGaussian.py
RENAMED
|
@@ -5,6 +5,7 @@ from cuqi.geometry import Continuous1D, Continuous2D, Image2D
|
|
|
5
5
|
from cuqi.operator import FirstOrderFiniteDifference
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
|
+
from copy import copy
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class RegularizedGaussian(Distribution):
|
|
@@ -269,29 +270,12 @@ class RegularizedGaussian(Distribution):
|
|
|
269
270
|
mutable_vars += ["strength"]
|
|
270
271
|
return mutable_vars
|
|
271
272
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
# Handle positional arguments (similar code as in Distribution._condition)
|
|
279
|
-
cond_vars = self.get_conditioning_variables()
|
|
280
|
-
kwargs = self._parse_args_add_to_kwargs(cond_vars, *args, **kwargs)
|
|
281
|
-
|
|
282
|
-
# When conditioning, we always do it on a copy to avoid unintentional side effects
|
|
283
|
-
new_density = self._make_copy()
|
|
284
|
-
|
|
285
|
-
# Check if self.name is in the provided keyword arguments.
|
|
286
|
-
# If so, pop it and store its value.
|
|
287
|
-
value = kwargs.pop(self.name, None)
|
|
288
|
-
|
|
289
|
-
new_density._gaussian = self.gaussian._condition(**kwargs)
|
|
290
|
-
|
|
291
|
-
# If self.name was provided, we convert to a likelihood or evaluated density
|
|
292
|
-
if value is not None:
|
|
293
|
-
new_density = new_density.to_likelihood(value)
|
|
294
|
-
|
|
273
|
+
def _make_copy(self):
|
|
274
|
+
""" Returns a shallow copy of the density keeping a pointer to the original. """
|
|
275
|
+
# Using deepcopy would also copy the underlying geometry, which causes a crash because geometries won't match anymore.
|
|
276
|
+
new_density = copy(self)
|
|
277
|
+
new_density._gaussian = copy(new_density._gaussian)
|
|
278
|
+
new_density._original_density = self
|
|
295
279
|
return new_density
|
|
296
280
|
|
|
297
281
|
|
|
@@ -45,9 +45,9 @@ class RegularizedUnboundedUniform(RegularizedGaussian):
|
|
|
45
45
|
|
|
46
46
|
regularization : string or None
|
|
47
47
|
Preset regularization. Can be set to "l1". Required for use in Gibbs in future update.
|
|
48
|
-
For "l1", the following additional parameters can be passed:
|
|
48
|
+
For "l1" or "tv", the following additional parameters can be passed:
|
|
49
49
|
strength : scalar
|
|
50
|
-
Regularization parameter, i.e., strength*||
|
|
50
|
+
Regularization parameter, i.e., strength*||Lx||_1, defaults to one
|
|
51
51
|
|
|
52
52
|
"""
|
|
53
53
|
def __init__(self, geometry, proximal = None, projector = None, constraint = None, regularization = None, **kwargs):
|
|
@@ -5,7 +5,7 @@ from typing import Tuple
|
|
|
5
5
|
|
|
6
6
|
import cuqi
|
|
7
7
|
from cuqi import config
|
|
8
|
-
from cuqi.distribution import Distribution, Gaussian, InverseGamma, LMRF, GMRF, Lognormal, Posterior, Beta, JointDistribution, Gamma, CMRF
|
|
8
|
+
from cuqi.distribution import Distribution, Gaussian, InverseGamma, LMRF, GMRF, Lognormal, Posterior, Beta, JointDistribution, Gamma, ModifiedHalfNormal, CMRF
|
|
9
9
|
from cuqi.implicitprior import RegularizedGaussian, RegularizedGMRF
|
|
10
10
|
from cuqi.density import Density
|
|
11
11
|
from cuqi.model import LinearModel, Model
|
|
@@ -923,8 +923,8 @@ class BayesianProblem(object):
|
|
|
923
923
|
if not isinstance(cond_target, Posterior):
|
|
924
924
|
raise NotImplementedError(f"Unable to determine sampling strategy for {par_name} with target {cond_target}")
|
|
925
925
|
|
|
926
|
-
# Gamma prior, Gaussian likelihood -> Conjugate
|
|
927
|
-
if self._check_posterior(cond_target, Gamma, (Gaussian, GMRF, RegularizedGaussian, RegularizedGMRF)):
|
|
926
|
+
# Gamma or ModifiedHalfNormal prior, Gaussian or RegularizedGaussian likelihood -> Conjugate
|
|
927
|
+
if self._check_posterior(cond_target, (Gamma, ModifiedHalfNormal), (Gaussian, GMRF, RegularizedGaussian, RegularizedGMRF)):
|
|
928
928
|
if experimental:
|
|
929
929
|
sampling_strategy[par_name] = cuqi.experimental.mcmc.Conjugate()
|
|
930
930
|
else:
|
|
@@ -12,7 +12,10 @@ from ._utilities import (
|
|
|
12
12
|
approx_derivative,
|
|
13
13
|
check_if_conditional_from_attr,
|
|
14
14
|
plot_1D_density,
|
|
15
|
-
plot_2D_density
|
|
15
|
+
plot_2D_density,
|
|
16
|
+
count_nonzero,
|
|
17
|
+
count_constant_components_1D,
|
|
18
|
+
count_constant_components_2D
|
|
16
19
|
)
|
|
17
20
|
|
|
18
21
|
from ._get_python_variable_name import _get_python_variable_name
|