CUQIpy 1.0.0.post0.dev337__tar.gz → 1.0.0.post0.dev360__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.

Files changed (116) hide show
  1. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/CUQIpy.egg-info/PKG-INFO +1 -1
  2. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/CUQIpy.egg-info/SOURCES.txt +2 -0
  3. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/PKG-INFO +1 -1
  4. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/_version.py +3 -3
  5. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/__init__.py +1 -0
  6. cuqipy-1.0.0.post0.dev360/cuqi/distribution/_modifiedhalfnormal.py +184 -0
  7. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/__init__.py +1 -0
  8. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_cwmh.py +3 -0
  9. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_mh.py +3 -1
  10. cuqipy-1.0.0.post0.dev360/cuqi/experimental/mcmc/_utilities.py +17 -0
  11. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_distribution.py +22 -0
  12. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_distributions_shape.py +3 -2
  13. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/CUQIpy.egg-info/dependency_links.txt +0 -0
  14. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/CUQIpy.egg-info/requires.txt +0 -0
  15. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/CUQIpy.egg-info/top_level.txt +0 -0
  16. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/LICENSE +0 -0
  17. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/README.md +0 -0
  18. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/__init__.py +0 -0
  19. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/_messages.py +0 -0
  20. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/array/__init__.py +0 -0
  21. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/array/_array.py +0 -0
  22. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/config.py +0 -0
  23. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/data/__init__.py +0 -0
  24. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/data/_data.py +0 -0
  25. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/data/astronaut.npz +0 -0
  26. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/data/camera.npz +0 -0
  27. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/data/cat.npz +0 -0
  28. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/data/cookie.png +0 -0
  29. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/data/satellite.mat +0 -0
  30. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/density/__init__.py +0 -0
  31. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/density/_density.py +0 -0
  32. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/diagnostics.py +0 -0
  33. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_beta.py +0 -0
  34. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_cauchy.py +0 -0
  35. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_cmrf.py +0 -0
  36. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_custom.py +0 -0
  37. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_distribution.py +0 -0
  38. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_gamma.py +0 -0
  39. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_gaussian.py +0 -0
  40. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_gmrf.py +0 -0
  41. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_inverse_gamma.py +0 -0
  42. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_joint_distribution.py +0 -0
  43. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_laplace.py +0 -0
  44. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_lmrf.py +0 -0
  45. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_lognormal.py +0 -0
  46. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_normal.py +0 -0
  47. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_posterior.py +0 -0
  48. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/distribution/_uniform.py +0 -0
  49. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/__init__.py +0 -0
  50. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_conjugate.py +0 -0
  51. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_conjugate_approx.py +0 -0
  52. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_direct.py +0 -0
  53. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_gibbs.py +0 -0
  54. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_hmc.py +0 -0
  55. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_langevin_algorithm.py +0 -0
  56. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_laplace_approximation.py +0 -0
  57. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_pcn.py +0 -0
  58. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_rto.py +0 -0
  59. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/experimental/mcmc/_sampler.py +0 -0
  60. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/geometry/__init__.py +0 -0
  61. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/geometry/_geometry.py +0 -0
  62. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/implicitprior/__init__.py +0 -0
  63. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/implicitprior/_regularizedGMRF.py +0 -0
  64. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/implicitprior/_regularizedGaussian.py +0 -0
  65. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/likelihood/__init__.py +0 -0
  66. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/likelihood/_likelihood.py +0 -0
  67. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/model/__init__.py +0 -0
  68. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/model/_model.py +0 -0
  69. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/operator/__init__.py +0 -0
  70. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/operator/_operator.py +0 -0
  71. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/pde/__init__.py +0 -0
  72. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/pde/_pde.py +0 -0
  73. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/problem/__init__.py +0 -0
  74. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/problem/_problem.py +0 -0
  75. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/__init__.py +0 -0
  76. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_conjugate.py +0 -0
  77. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_conjugate_approx.py +0 -0
  78. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_cwmh.py +0 -0
  79. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_gibbs.py +0 -0
  80. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_hmc.py +0 -0
  81. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_langevin_algorithm.py +0 -0
  82. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_laplace_approximation.py +0 -0
  83. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_mh.py +0 -0
  84. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_pcn.py +0 -0
  85. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_rto.py +0 -0
  86. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/sampler/_sampler.py +0 -0
  87. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/samples/__init__.py +0 -0
  88. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/samples/_samples.py +0 -0
  89. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/solver/__init__.py +0 -0
  90. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/solver/_solver.py +0 -0
  91. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/testproblem/__init__.py +0 -0
  92. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/testproblem/_testproblem.py +0 -0
  93. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/utilities/__init__.py +0 -0
  94. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/utilities/_get_python_variable_name.py +0 -0
  95. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/cuqi/utilities/_utilities.py +0 -0
  96. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/pyproject.toml +0 -0
  97. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/requirements.txt +0 -0
  98. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/setup.cfg +0 -0
  99. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/setup.py +0 -0
  100. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_MRFs.py +0 -0
  101. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_abstract_distribution_density.py +0 -0
  102. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_bayesian_inversion.py +0 -0
  103. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_density.py +0 -0
  104. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_geometry.py +0 -0
  105. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_implicit_priors.py +0 -0
  106. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_joint_distribution.py +0 -0
  107. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_likelihood.py +0 -0
  108. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_model.py +0 -0
  109. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_pde.py +0 -0
  110. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_posterior.py +0 -0
  111. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_problem.py +0 -0
  112. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_sampler.py +0 -0
  113. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_samples.py +0 -0
  114. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_solver.py +0 -0
  115. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_testproblem.py +0 -0
  116. {cuqipy-1.0.0.post0.dev337 → cuqipy-1.0.0.post0.dev360}/tests/test_utilities.py +0 -0
@@ -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.dev360
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
@@ -38,6 +38,7 @@ cuqi/distribution/_joint_distribution.py
38
38
  cuqi/distribution/_laplace.py
39
39
  cuqi/distribution/_lmrf.py
40
40
  cuqi/distribution/_lognormal.py
41
+ cuqi/distribution/_modifiedhalfnormal.py
41
42
  cuqi/distribution/_normal.py
42
43
  cuqi/distribution/_posterior.py
43
44
  cuqi/distribution/_uniform.py
@@ -55,6 +56,7 @@ cuqi/experimental/mcmc/_mh.py
55
56
  cuqi/experimental/mcmc/_pcn.py
56
57
  cuqi/experimental/mcmc/_rto.py
57
58
  cuqi/experimental/mcmc/_sampler.py
59
+ cuqi/experimental/mcmc/_utilities.py
58
60
  cuqi/geometry/__init__.py
59
61
  cuqi/geometry/_geometry.py
60
62
  cuqi/implicitprior/__init__.py
@@ -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.dev360
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": "2024-06-11T10:55:36+0200",
11
+ "date": "2024-07-01T14:33:09+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "b3c53f2ac21c807186dc35053421615c1cb3f205",
15
- "version": "1.0.0.post0.dev337"
14
+ "full-revisionid": "4da44104c9163182cd1eeb31fb4678a2893111fe",
15
+ "version": "1.0.0.post0.dev360"
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
+
@@ -12,3 +12,4 @@ from ._gibbs import HybridGibbsNew
12
12
  from ._conjugate import ConjugateNew
13
13
  from ._conjugate_approx import ConjugateApproxNew
14
14
  from ._direct import DirectNew
15
+ from ._utilities import find_valid_samplers
@@ -100,6 +100,9 @@ class CWMHNew(ProposalBasedSamplerNew):
100
100
  raise ValueError(
101
101
  "Target should be an instance of "+\
102
102
  f"{cuqi.density.Density.__class__.__name__}")
103
+ # Fail when there is no log density, which is currently assumed to be the case in case NaN is returned.
104
+ if np.isnan(self.target.logd(self._default_initial_point)):
105
+ raise ValueError("Target does not have valid logd")
103
106
 
104
107
  def validate_proposal(self):
105
108
  if not isinstance(self.proposal, cuqi.distribution.Distribution):
@@ -32,7 +32,9 @@ class MHNew(ProposalBasedSamplerNew):
32
32
  self._scale_temp = self.scale
33
33
 
34
34
  def validate_target(self):
35
- pass # All targets are valid
35
+ # Fail only when there is no log density, which is currently assumed to be the case in case NaN is returned.
36
+ if np.isnan(self.target.logd(self._default_initial_point)):
37
+ raise ValueError("Target does not have valid logd")
36
38
 
37
39
  def validate_proposal(self):
38
40
  if not isinstance(self.proposal, cuqi.distribution.Distribution):
@@ -0,0 +1,17 @@
1
+ import cuqi
2
+ import inspect
3
+
4
+ def find_valid_samplers(target):
5
+ """ Finds all samplers in the cuqi.experimental.mcmc module that accept the provided target. """
6
+
7
+ all_samplers = [(name, cls) for name, cls in inspect.getmembers(cuqi.experimental.mcmc, inspect.isclass) if issubclass(cls, cuqi.experimental.mcmc.SamplerNew)]
8
+ valid_samplers = []
9
+
10
+ for name, sampler in all_samplers:
11
+ try:
12
+ sampler(target)
13
+ valid_samplers += [name]
14
+ except:
15
+ pass
16
+
17
+ return valid_samplers
@@ -758,3 +758,25 @@ def test_Gaussian_from_linear_operator_sqrtprec():
758
758
  y_from_dense = cuqi.distribution.Gaussian(mean = np.zeros(N), sqrtprec = sqrtprec.todense())
759
759
 
760
760
  assert np.allclose(y_from_dense.logpdf(np.ones(N)), y_from_sparse.logpdf(np.ones(N)))
761
+
762
+ @pytest.mark.parametrize("alpha, beta, gamma, expected",[
763
+ (1.0, 2.0, 3.0, [0.77974597, 0.77361298, 0.5422682, 0.81054637, 1.35205349]),
764
+ (128.0, 3.0, -4.0, [1.02250649, 1.05735833, 0.98794758, 1.04533337, 1.00500678])
765
+ ])
766
+ def test_MHN_sample_regression(alpha, beta, gamma, expected):
767
+ rng = np.random.RandomState(0)
768
+ dist = cuqi.distribution.ModifiedHalfNormal(alpha, beta, gamma)
769
+ samples = dist.sample(5,rng=rng).samples
770
+ assert np.allclose( samples, np.array(expected))
771
+
772
+ @pytest.mark.parametrize("alpha, beta, gamma, expected_logpdf, expected_gradient",[
773
+ (1.0, 2.0, 3.0, -40.0, [[-1.], [-3.], [-5.], [-7.], [-9.]]),
774
+ (64.0, 3.0, -4.0, -2258.388020204731, [[-1.], [-160.5], [-299.], [-432.25], [-563.4 ]])
775
+ ])
776
+ def test_MHN_regression(alpha, beta, gamma, expected_logpdf, expected_gradient):
777
+ rng = np.random.RandomState(0)
778
+ dist = cuqi.distribution.ModifiedHalfNormal(alpha, beta, gamma)
779
+ logpdf = dist.logpdf(np.array([1.0, 2.0, 3.0, 4.0, 5.0]))
780
+ gradient = dist._gradient(np.array([1.0, 2.0, 3.0, 4.0, 5.0]))
781
+ assert np.allclose( logpdf, np.array(expected_logpdf))
782
+ assert np.allclose( gradient, np.array(expected_gradient))
@@ -17,7 +17,7 @@ ignore_list = [
17
17
 
18
18
  # Define cases to skip (these are TODO)
19
19
  skip_logd = [
20
- cuqi.distribution.Gamma # Missing force_ndarray
20
+ cuqi.distribution.Gamma, # Missing force_ndarray
21
21
  ]
22
22
  skip_sample = [
23
23
  cuqi.distribution.Gamma, # Missing force_ndarray
@@ -67,7 +67,8 @@ def test_multivariate_scalar_vars_logd(dist):
67
67
  val = np.random.randn(5)
68
68
  assert np.allclose(
69
69
  dist_from_vec.logd(val),
70
- dist_from_dim.logd(val)
70
+ dist_from_dim.logd(val),
71
+ equal_nan=True
71
72
  )
72
73
 
73
74