CUQIpy 1.0.0.post0.dev215__tar.gz → 1.0.0.post0.dev305__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 (111) hide show
  1. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/CUQIpy.egg-info/PKG-INFO +1 -1
  2. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/PKG-INFO +1 -1
  3. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/_version.py +3 -3
  4. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/experimental/mcmc/__init__.py +1 -1
  5. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/experimental/mcmc/_cwmh.py +29 -42
  6. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/experimental/mcmc/_hmc.py +34 -34
  7. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/experimental/mcmc/_langevin_algorithm.py +5 -4
  8. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/experimental/mcmc/_laplace_approximation.py +10 -12
  9. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/experimental/mcmc/_mh.py +8 -15
  10. cuqipy-1.0.0.post0.dev305/cuqi/experimental/mcmc/_pcn.py +89 -0
  11. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/experimental/mcmc/_rto.py +24 -51
  12. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/experimental/mcmc/_sampler.py +172 -56
  13. cuqipy-1.0.0.post0.dev215/cuqi/experimental/mcmc/_pcn.py +0 -91
  14. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/CUQIpy.egg-info/SOURCES.txt +0 -0
  15. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/CUQIpy.egg-info/dependency_links.txt +0 -0
  16. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/CUQIpy.egg-info/requires.txt +0 -0
  17. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/CUQIpy.egg-info/top_level.txt +0 -0
  18. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/LICENSE +0 -0
  19. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/README.md +0 -0
  20. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/__init__.py +0 -0
  21. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/_messages.py +0 -0
  22. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/array/__init__.py +0 -0
  23. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/array/_array.py +0 -0
  24. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/config.py +0 -0
  25. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/data/__init__.py +0 -0
  26. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/data/_data.py +0 -0
  27. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/data/astronaut.npz +0 -0
  28. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/data/camera.npz +0 -0
  29. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/data/cat.npz +0 -0
  30. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/data/cookie.png +0 -0
  31. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/data/satellite.mat +0 -0
  32. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/density/__init__.py +0 -0
  33. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/density/_density.py +0 -0
  34. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/diagnostics.py +0 -0
  35. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/__init__.py +0 -0
  36. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_beta.py +0 -0
  37. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_cauchy.py +0 -0
  38. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_cmrf.py +0 -0
  39. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_custom.py +0 -0
  40. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_distribution.py +0 -0
  41. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_gamma.py +0 -0
  42. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_gaussian.py +0 -0
  43. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_gmrf.py +0 -0
  44. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_inverse_gamma.py +0 -0
  45. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_joint_distribution.py +0 -0
  46. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_laplace.py +0 -0
  47. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_lmrf.py +0 -0
  48. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_lognormal.py +0 -0
  49. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_normal.py +0 -0
  50. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_posterior.py +0 -0
  51. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/distribution/_uniform.py +0 -0
  52. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/experimental/__init__.py +0 -0
  53. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/geometry/__init__.py +0 -0
  54. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/geometry/_geometry.py +0 -0
  55. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/implicitprior/__init__.py +0 -0
  56. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/implicitprior/_regularizedGMRF.py +0 -0
  57. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/implicitprior/_regularizedGaussian.py +0 -0
  58. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/likelihood/__init__.py +0 -0
  59. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/likelihood/_likelihood.py +0 -0
  60. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/model/__init__.py +0 -0
  61. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/model/_model.py +0 -0
  62. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/operator/__init__.py +0 -0
  63. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/operator/_operator.py +0 -0
  64. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/pde/__init__.py +0 -0
  65. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/pde/_pde.py +0 -0
  66. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/problem/__init__.py +0 -0
  67. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/problem/_problem.py +0 -0
  68. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/__init__.py +0 -0
  69. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_conjugate.py +0 -0
  70. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_conjugate_approx.py +0 -0
  71. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_cwmh.py +0 -0
  72. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_gibbs.py +0 -0
  73. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_hmc.py +0 -0
  74. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_langevin_algorithm.py +0 -0
  75. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_laplace_approximation.py +0 -0
  76. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_mh.py +0 -0
  77. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_pcn.py +0 -0
  78. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_rto.py +0 -0
  79. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/sampler/_sampler.py +0 -0
  80. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/samples/__init__.py +0 -0
  81. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/samples/_samples.py +0 -0
  82. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/solver/__init__.py +0 -0
  83. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/solver/_solver.py +0 -0
  84. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/testproblem/__init__.py +0 -0
  85. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/testproblem/_testproblem.py +0 -0
  86. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/utilities/__init__.py +0 -0
  87. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/utilities/_get_python_variable_name.py +0 -0
  88. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/cuqi/utilities/_utilities.py +0 -0
  89. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/pyproject.toml +0 -0
  90. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/requirements.txt +0 -0
  91. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/setup.cfg +0 -0
  92. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/setup.py +0 -0
  93. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_MRFs.py +0 -0
  94. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_abstract_distribution_density.py +0 -0
  95. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_bayesian_inversion.py +0 -0
  96. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_density.py +0 -0
  97. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_distribution.py +0 -0
  98. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_distributions_shape.py +0 -0
  99. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_geometry.py +0 -0
  100. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_implicit_priors.py +0 -0
  101. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_joint_distribution.py +0 -0
  102. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_likelihood.py +0 -0
  103. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_model.py +0 -0
  104. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_pde.py +0 -0
  105. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_posterior.py +0 -0
  106. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_problem.py +0 -0
  107. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_sampler.py +0 -0
  108. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_samples.py +0 -0
  109. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_solver.py +0 -0
  110. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/tests/test_testproblem.py +0 -0
  111. {cuqipy-1.0.0.post0.dev215 → cuqipy-1.0.0.post0.dev305}/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.dev215
3
+ Version: 1.0.0.post0.dev305
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.1
2
2
  Name: CUQIpy
3
- Version: 1.0.0.post0.dev215
3
+ Version: 1.0.0.post0.dev305
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-05-16T11:29:50+0200",
11
+ "date": "2024-05-27T07:34:21+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "b713d6478d33977b12342e4bcb29968a95ee5973",
15
- "version": "1.0.0.post0.dev215"
14
+ "full-revisionid": "1274ccfff1cb9663b547dd2aeb9c86c72dccb2ae",
15
+ "version": "1.0.0.post0.dev305"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -3,7 +3,7 @@
3
3
  from ._sampler import SamplerNew, ProposalBasedSamplerNew
4
4
  from ._langevin_algorithm import ULANew, MALANew
5
5
  from ._mh import MHNew
6
- from ._pcn import pCNNew
6
+ from ._pcn import PCNNew
7
7
  from ._rto import LinearRTONew, RegularizedLinearRTONew
8
8
  from ._cwmh import CWMHNew
9
9
  from ._laplace_approximation import UGLANew
@@ -67,16 +67,21 @@ class CWMHNew(ProposalBasedSamplerNew):
67
67
  samples = sampler.sample(2000).get_samples()
68
68
 
69
69
  """
70
- def __init__(self, target: cuqi.density.Density, proposal=None, scale=1,
70
+
71
+ _STATE_KEYS = ProposalBasedSamplerNew._STATE_KEYS.union(['_scale_temp'])
72
+
73
+ def __init__(self, target:cuqi.density.Density=None, proposal=None, scale=1,
71
74
  initial_point=None, **kwargs):
72
75
  super().__init__(target, proposal=proposal, scale=scale,
73
76
  initial_point=initial_point, **kwargs)
77
+
78
+ def _initialize(self):
79
+ if isinstance(self.scale, Number):
80
+ self.scale = np.ones(self.dim)*self.scale
81
+ self._acc = [np.ones((self.dim))] # Overwrite acc from ProposalBasedSamplerNew with list of arrays
74
82
 
75
- # set initial scale
76
- self.scale = scale
77
-
78
- # set initial acceptance rate
79
- self._acc = [np.ones((self.dim))]
83
+ # Handling of temporary scale parameter due to possible bug in old CWMH
84
+ self._scale_temp = self.scale.copy()
80
85
 
81
86
  @property
82
87
  def scale(self):
@@ -86,53 +91,35 @@ class CWMHNew(ProposalBasedSamplerNew):
86
91
  @scale.setter
87
92
  def scale(self, value):
88
93
  """ Set the scale parameter. """
89
- if isinstance(value, Number):
90
- self._scale = np.ones(self.dim)*value
91
- elif isinstance(value, np.ndarray):
92
- self._scale = value
93
- self._scale_temp = self._scale.copy()
94
+ if self._is_initialized and isinstance(value, Number):
95
+ value = np.ones(self.dim)*value
96
+ self._scale = value
94
97
 
95
98
  def validate_target(self):
96
99
  if not isinstance(self.target, cuqi.density.Density):
97
100
  raise ValueError(
98
101
  "Target should be an instance of "+\
99
102
  f"{cuqi.density.Density.__class__.__name__}")
100
-
101
- @ProposalBasedSamplerNew.proposal.setter
102
- # TODO. Check if we can refactor this.
103
- # We can work with a validate_proposal method instead?
104
- def proposal(self, value):
105
- fail_msg = "Proposal should be either None, "+\
106
- f"{cuqi.distribution.Distribution.__class__.__name__} "+\
107
- "conditioned only on 'location' and 'scale', lambda function, "+\
108
- f"or {cuqi.distribution.Normal.__class__.__name__} conditioned "+\
109
- "only on 'mean' and 'std'"
110
-
111
- if value is None:
103
+
104
+ def validate_proposal(self):
105
+ if not isinstance(self.proposal, cuqi.distribution.Distribution):
106
+ raise ValueError("Proposal must be a cuqi.distribution.Distribution object")
107
+ if not self.proposal.is_symmetric:
108
+ raise ValueError("Proposal must be symmetric")
109
+
110
+ @property
111
+ def proposal(self):
112
+ if self._proposal is None:
112
113
  self._proposal = cuqi.distribution.Normal(
113
114
  mean=lambda location: location,
114
115
  std=lambda scale: scale,
115
116
  geometry=self.dim,
116
117
  )
117
-
118
- elif isinstance(value, cuqi.distribution.Distribution) and sorted(
119
- value.get_conditioning_variables()
120
- ) == ["location", "scale"]:
121
- self._proposal = value
122
-
123
- elif isinstance(value, cuqi.distribution.Normal) and sorted(
124
- value.get_conditioning_variables()
125
- ) == ["mean", "std"]:
126
- self._proposal = value(
127
- mean=lambda location: location, std=lambda scale: scale
128
- )
129
-
130
- elif not isinstance(value, cuqi.distribution.Distribution) and callable(
131
- value):
132
- self._proposal = value
133
-
134
- else:
135
- raise ValueError(fail_msg)
118
+ return self._proposal
119
+
120
+ @proposal.setter
121
+ def proposal(self, value):
122
+ self._proposal = value
136
123
 
137
124
  def step(self):
138
125
  # Initialize x_t which is used to store the current CWMH sample
@@ -95,7 +95,7 @@ class NUTSNew(SamplerNew):
95
95
  'epsilon_list',
96
96
  'epsilon_bar_list'})
97
97
 
98
- def __init__(self, target, initial_point=None, max_depth=15,
98
+ def __init__(self, target=None, initial_point=None, max_depth=15,
99
99
  step_size=None, opt_acc_rate=0.6, **kwargs):
100
100
  super().__init__(target, initial_point=initial_point, **kwargs)
101
101
 
@@ -103,9 +103,6 @@ class NUTSNew(SamplerNew):
103
103
  self.max_depth = max_depth
104
104
  self.step_size = step_size
105
105
  self.opt_acc_rate = opt_acc_rate
106
-
107
- # Set current point
108
- self.current_point = self.initial_point
109
106
 
110
107
  # Initialize epsilon and epsilon_bar
111
108
  # epsilon is the step size used in the current iteration
@@ -115,12 +112,37 @@ class NUTSNew(SamplerNew):
115
112
  self._epsilon_bar = None
116
113
  self._H_bar = None
117
114
 
115
+ # Extra parameters for tuning
116
+ self._n_alpha = None
117
+ self._alpha = None
118
+
119
+
120
+ def _initialize(self):
121
+
118
122
  # Arrays to store acceptance rate
119
- self._acc = [None]
123
+ self._acc = [None] # Overwrites acc from SamplerNew. TODO. Check if this is necessary
124
+
125
+ self._alpha = 0 # check if meaningful value
126
+ self._n_alpha = 0 # check if meaningful value
127
+
128
+ self.current_target_logd, self.current_target_grad = self._nuts_target(self.current_point)
129
+
130
+ # parameters dual averaging
131
+ if self.step_size is None:
132
+ self._epsilon = self._FindGoodEpsilon()
133
+ else:
134
+ self._epsilon = self.step_size
135
+ self._epsilon_bar = "unset"
136
+
137
+ # Parameter mu, does not change during the run
138
+ self._mu = np.log(10*self._epsilon)
139
+
140
+ self._H_bar = 0
120
141
 
121
142
  # NUTS run diagnostic:
122
143
  # number of tree nodes created each NUTS iteration
123
144
  self._num_tree_node = 0
145
+
124
146
  # Create lists to store NUTS run diagnostics
125
147
  self._create_run_diagnostic_attributes()
126
148
 
@@ -176,9 +198,9 @@ class NUTSNew(SamplerNew):
176
198
  except:
177
199
  raise ValueError('Target must have logd and gradient methods.')
178
200
 
179
- def reset(self):
201
+ def reinitialize(self):
180
202
  # Call the parent reset method
181
- super().reset()
203
+ super().reinitialize()
182
204
  # Reset NUTS run diagnostic attributes
183
205
  self._reset_run_diagnostic_attributes()
184
206
 
@@ -275,10 +297,6 @@ class NUTSNew(SamplerNew):
275
297
  np.exp(eta*np.log(self._epsilon) +(1-eta)*np.log(self._epsilon_bar))
276
298
 
277
299
  def _pre_warmup(self):
278
- super()._pre_warmup()
279
-
280
- self.current_target_logd, self.current_target_grad =\
281
- self._nuts_target(self.current_point)
282
300
 
283
301
  # Set up tuning parameters (only first time tuning is called)
284
302
  # Note:
@@ -289,32 +307,14 @@ class NUTSNew(SamplerNew):
289
307
  # Parameters that does not change during the run
290
308
  # self._mu
291
309
 
292
- if self._epsilon is None:
293
- # parameters dual averaging
294
- self._epsilon = self._FindGoodEpsilon()
295
- # Parameter mu, does not change during the run
296
- self._mu = np.log(10*self._epsilon)
297
-
298
- if self._epsilon_bar is None: # Initial value of epsilon_bar
310
+ if self._epsilon_bar == "unset": # Initial value of epsilon_bar for tuning
299
311
  self._epsilon_bar = 1
300
312
 
301
- if self._H_bar is None: # Initial value of H_bar
302
- self._H_bar = 0
303
-
304
313
  def _pre_sample(self):
305
- super()._pre_sample()
306
314
 
307
- self.current_target_logd, self.current_target_grad =\
308
- self._nuts_target(self.current_point)
309
-
310
- # Set up epsilon and epsilon_bar if not set
311
- if self._epsilon is None:
312
- if self.step_size is None:
313
- step_size = self._FindGoodEpsilon()
314
- else:
315
- step_size = self.step_size
316
- self._epsilon = step_size
317
- self._epsilon_bar = step_size
315
+ if self._epsilon_bar == "unset": # Initial value of epsilon_bar for sampling
316
+ self._epsilon_bar = self._epsilon
317
+
318
318
 
319
319
  #=========================================================================
320
320
  def _nuts_target(self, x): # returns logposterior tuple evaluation-gradient
@@ -467,4 +467,4 @@ class NUTSNew(SamplerNew):
467
467
  # Store the step size used in iteration k
468
468
  self.epsilon_list.append(eps)
469
469
  # Store the step size suggestion during adaptation in iteration k
470
- self.epsilon_bar_list.append(eps_bar)
470
+ self.epsilon_bar_list.append(eps_bar)
@@ -63,15 +63,16 @@ class ULANew(SamplerNew): # Refactor to Proposal-based sampler?
63
63
 
64
64
  _STATE_KEYS = SamplerNew._STATE_KEYS.union({'current_target_logd', 'scale', 'current_target_grad'})
65
65
 
66
- def __init__(self, target, scale=1.0, **kwargs):
66
+ def __init__(self, target=None, scale=1.0, **kwargs):
67
67
 
68
68
  super().__init__(target, **kwargs)
69
69
 
70
- self.scale = scale
71
- self.current_point = self.initial_point
70
+ self.initial_scale = scale
71
+
72
+ def _initialize(self):
73
+ self.scale = self.initial_scale
72
74
  self.current_target_logd = self.target.logd(self.current_point)
73
75
  self.current_target_grad = self.target.gradient(self.current_point)
74
- self._acc = [1] # TODO. Check if we need this
75
76
 
76
77
  def validate_target(self):
77
78
  try:
@@ -53,17 +53,13 @@ class UGLANew(SamplerNew):
53
53
 
54
54
  super().__init__(target=target, initial_point=initial_point, **kwargs)
55
55
 
56
- if initial_point is None: #TODO: Replace later with a getter
57
- self.initial_point = np.zeros(self.dim)
58
- self._samples = [self.initial_point]
59
-
60
- self.current_point = self.initial_point
61
- self._acc = [1] # TODO. Check if we need this
62
-
63
56
  # Parameters
64
57
  self.maxit = maxit
65
58
  self.tol = tol
66
59
  self.beta = beta
60
+
61
+ def _initialize(self):
62
+ self._precompute()
67
63
 
68
64
  @property
69
65
  def prior(self):
@@ -81,8 +77,8 @@ class UGLANew(SamplerNew):
81
77
  def data(self):
82
78
  return self.target.data
83
79
 
84
- def _pre_warmup(self):
85
- super()._pre_warmup()
80
+ def _precompute(self):
81
+
86
82
  D = self.prior._diff_op
87
83
  n = D.shape[0]
88
84
 
@@ -121,9 +117,6 @@ class UGLANew(SamplerNew):
121
117
  return out
122
118
  self.M = M
123
119
 
124
- def _pre_sample(self):
125
- self._pre_warmup()
126
-
127
120
  def step(self):
128
121
  # Update Laplace approximation
129
122
  self._L2 = self.Lk_fun(self.current_point)
@@ -157,3 +150,8 @@ class UGLANew(SamplerNew):
157
150
  # Check that prior is LMRF
158
151
  if not isinstance(self.prior, cuqi.distribution.LMRF):
159
152
  raise ValueError('Unadjusted Gaussian Laplace approximation (UGLA) requires LMRF prior')
153
+
154
+ @property
155
+ def _default_initial_point(self):
156
+ """ Get the default initial point for the sampler. Defaults to an array of zeros. """
157
+ return np.zeros(self.dim)
@@ -1,7 +1,6 @@
1
1
  import numpy as np
2
2
  import cuqi
3
3
  from cuqi.experimental.mcmc import ProposalBasedSamplerNew
4
- from cuqi.array import CUQIarray
5
4
 
6
5
 
7
6
  class MHNew(ProposalBasedSamplerNew):
@@ -27,25 +26,19 @@ class MHNew(ProposalBasedSamplerNew):
27
26
 
28
27
  def __init__(self, target, proposal=None, scale=1, **kwargs):
29
28
  super().__init__(target, proposal=proposal, scale=scale, **kwargs)
29
+
30
+ def _initialize(self):
30
31
  # Due to a bug? in old MH, we must keep track of this extra variable to match behavior.
31
- self._scale_temp = self.scale
32
+ self._scale_temp = self.scale
32
33
 
33
34
  def validate_target(self):
34
35
  pass # All targets are valid
35
36
 
36
- @ProposalBasedSamplerNew.proposal.setter # TODO. Check if we can refactor this. We can work with a validate_proposal method instead?
37
- def proposal(self, value):
38
- fail_msg = "Proposal should be either None, symmetric cuqi.distribution.Distribution or a lambda function."
39
-
40
- if value is None:
41
- self._proposal = cuqi.distribution.Gaussian(np.zeros(self.dim), 1)
42
- elif not isinstance(value, cuqi.distribution.Distribution) and callable(value):
43
- raise NotImplementedError(fail_msg)
44
- elif isinstance(value, cuqi.distribution.Distribution) and value.is_symmetric:
45
- self._proposal = value
46
- else:
47
- raise ValueError(fail_msg)
48
- self._proposal.geometry = self.target.geometry
37
+ def validate_proposal(self):
38
+ if not isinstance(self.proposal, cuqi.distribution.Distribution):
39
+ raise ValueError("Proposal must be a cuqi.distribution.Distribution object")
40
+ if not self.proposal.is_symmetric:
41
+ raise ValueError("Proposal must be symmetric")
49
42
 
50
43
  def step(self):
51
44
  # propose state
@@ -0,0 +1,89 @@
1
+ import numpy as np
2
+ import cuqi
3
+ from cuqi.experimental.mcmc import SamplerNew
4
+ from cuqi.array import CUQIarray
5
+
6
+ class PCNNew(SamplerNew): # Refactor to Proposal-based sampler?
7
+
8
+ _STATE_KEYS = SamplerNew._STATE_KEYS.union({'scale', 'current_likelihood_logd', 'lambd'})
9
+
10
+ def __init__(self, target=None, scale=1.0, **kwargs):
11
+
12
+ super().__init__(target, **kwargs)
13
+ self.initial_scale = scale
14
+
15
+ def _initialize(self):
16
+ self.scale = self.initial_scale
17
+ self.current_likelihood_logd = self._loglikelihood(self.current_point)
18
+
19
+ # parameters used in the Robbins-Monro recursion for tuning the scale parameter
20
+ # see details and reference in the tune method
21
+ self.lambd = self.scale
22
+ self.star_acc = 0.44 #TODO: 0.234 # target acceptance rate
23
+
24
+ def validate_target(self):
25
+ if not isinstance(self.target, cuqi.distribution.Posterior):
26
+ raise ValueError(f"To initialize an object of type {self.__class__}, 'target' need to be of type 'cuqi.distribution.Posterior'.")
27
+ if not isinstance(self.prior, (cuqi.distribution.Gaussian, cuqi.distribution.Normal)):
28
+ raise ValueError("The prior distribution of the target need to be Gaussian")
29
+
30
+ def step(self):
31
+ # propose state
32
+ xi = self.prior.sample(1).flatten() # sample from the prior
33
+ x_star = np.sqrt(1-self.scale**2)*self.current_point + self.scale*xi # PCN proposal
34
+
35
+ # evaluate target
36
+ loglike_eval_star = self._loglikelihood(x_star)
37
+
38
+ # ratio and acceptance probability
39
+ ratio = loglike_eval_star - self.current_likelihood_logd # proposal is symmetric
40
+ alpha = min(0, ratio)
41
+
42
+ # accept/reject
43
+ acc = 0
44
+ u_theta = np.log(np.random.rand())
45
+ if (u_theta <= alpha):
46
+ self.current_point = x_star
47
+ self.current_likelihood_logd = loglike_eval_star
48
+ acc = 1
49
+
50
+ return acc
51
+
52
+ @property
53
+ def prior(self):
54
+ return self.target.prior
55
+
56
+ @property
57
+ def likelihood(self):
58
+ return self.target.likelihood
59
+
60
+ def _loglikelihood(self, x):
61
+ return self.likelihood.logd(x)
62
+
63
+ @property
64
+ def dim(self): # TODO. Check if we need this. Implemented in base class
65
+ if hasattr(self,'target') and hasattr(self.target,'dim'):
66
+ self._dim = self.target.dim
67
+ elif hasattr(self,'target') and isinstance(self.target,tuple) and len(self.target)==2:
68
+ self._dim = self.target[0].dim
69
+ return self._dim
70
+
71
+ def tune(self, skip_len, update_count):
72
+ """
73
+ Tune the scale parameter of the PCN sampler.
74
+ The tuning is based on algorithm 4 in Andrieu, Christophe, and Johannes Thoms.
75
+ "A tutorial on adaptive MCMC." Statistics and computing 18 (2008): 343-373.
76
+ Note: the tuning algorithm here is the same as the one used in MH sampler.
77
+ """
78
+
79
+ # average acceptance rate in the past skip_len iterations
80
+ hat_acc = np.mean(self._acc[-skip_len:])
81
+
82
+ # new scaling parameter zeta to be used in the Robbins-Monro recursion
83
+ zeta = 1/np.sqrt(update_count+1)
84
+
85
+ # Robbins-Monro recursion to ensure that the variation of lambd vanishes
86
+ self.lambd = np.exp(np.log(self.lambd) + zeta*(hat_acc-self.star_acc))
87
+
88
+ # update scale parameter
89
+ self.scale = min(self.lambd, 1)
@@ -5,7 +5,6 @@ import numpy as np
5
5
  import cuqi
6
6
  from cuqi.solver import CGLS, FISTA
7
7
  from cuqi.experimental.mcmc import SamplerNew
8
- from cuqi.array import CUQIarray
9
8
 
10
9
 
11
10
  class LinearRTONew(SamplerNew):
@@ -44,21 +43,17 @@ class LinearRTONew(SamplerNew):
44
43
  An example is shown in demos/demo31_callback.py.
45
44
 
46
45
  """
47
- def __init__(self, target, initial_point=None, maxit=10, tol=1e-6, **kwargs):
46
+ def __init__(self, target=None, initial_point=None, maxit=10, tol=1e-6, **kwargs):
48
47
 
49
48
  super().__init__(target=target, initial_point=initial_point, **kwargs)
50
49
 
51
- if initial_point is None: #TODO: Replace later with a getter
52
- self.initial_point = np.zeros(self.dim)
53
- self._samples = [self.initial_point]
54
-
55
- self.current_point = self.initial_point
56
- self._acc = [1] # TODO. Check if we need this
57
-
58
50
  # Other parameters
59
51
  self.maxit = maxit
60
52
  self.tol = tol
61
53
 
54
+ def _initialize(self):
55
+ self._precompute()
56
+
62
57
  @property
63
58
  def prior(self):
64
59
  return self.target.prior
@@ -81,41 +76,7 @@ class LinearRTONew(SamplerNew):
81
76
  @property
82
77
  def data(self):
83
78
  return self.target.data
84
-
85
- @SamplerNew.target.setter
86
- def target(self, value):
87
- """ Set the target density. Runs validation of the target. """
88
- # Accept tuple of inputs and construct posterior
89
- if isinstance(value, tuple) and len(value) == 5:
90
- # Structure (data, model, L_sqrtprec, P_mean, P_sqrtprec)
91
- data = value[0]
92
- model = value[1]
93
- L_sqrtprec = value[2]
94
- P_mean = value[3]
95
- P_sqrtprec = value[4]
96
-
97
- # If numpy matrix convert to CUQI model
98
- if isinstance(model, np.ndarray) and len(model.shape) == 2:
99
- model = cuqi.model.LinearModel(model)
100
-
101
- # Check model input
102
- if not isinstance(model, cuqi.model.LinearModel):
103
- raise TypeError("Model needs to be cuqi.model.LinearModel or matrix")
104
-
105
- # Likelihood
106
- L = cuqi.distribution.Gaussian(model, sqrtprec=L_sqrtprec).to_likelihood(data)
107
-
108
- # Prior TODO: allow multiple priors stacked
109
- #if isinstance(P_mean, list) and isinstance(P_sqrtprec, list):
110
- # P = cuqi.distribution.JointGaussianSqrtPrec(P_mean, P_sqrtprec)
111
- #else:
112
- P = cuqi.distribution.Gaussian(P_mean, sqrtprec=P_sqrtprec)
113
-
114
- # Construct posterior
115
- value = cuqi.distribution.Posterior(L, P)
116
- super(LinearRTONew, type(self)).target.fset(self, value)
117
- self._precompute()
118
-
79
+
119
80
  def _precompute(self):
120
81
  L1 = [likelihood.distribution.sqrtprec for likelihood in self.likelihoods]
121
82
  L2 = self.prior.sqrtprec
@@ -188,6 +149,11 @@ class LinearRTONew(SamplerNew):
188
149
 
189
150
  if not hasattr(self.prior, "sqrtprecTimesMean"):
190
151
  raise TypeError("Prior must contain a sqrtprecTimesMean attribute")
152
+
153
+ @property
154
+ def _default_initial_point(self):
155
+ """ Get the default initial point for the sampler. Defaults to an array of zeros. """
156
+ return np.zeros(self.dim)
191
157
 
192
158
  class RegularizedLinearRTONew(LinearRTONew):
193
159
  """
@@ -231,15 +197,22 @@ class RegularizedLinearRTONew(LinearRTONew):
231
197
  self.stepsize = stepsize
232
198
  self.abstol = abstol
233
199
  self.adaptive = adaptive
234
- self.proximal = target.prior.proximal
235
- self._stepsize = self._choose_stepsize()
236
200
  self.maxit = maxit
237
201
 
238
- @LinearRTONew.target.setter
239
- def target(self, value):
240
- if not callable(value.prior.proximal):
241
- raise TypeError("Projector needs to be callable")
242
- return super(RegularizedLinearRTONew, type(self)).target.fset(self, value)
202
+ def _initialize(self):
203
+ super()._initialize()
204
+ self._stepsize = self._choose_stepsize()
205
+
206
+ @property
207
+ def proximal(self):
208
+ return self.target.prior.proximal
209
+
210
+ def validate_target(self):
211
+ super().validate_target()
212
+ if not isinstance(self.target.prior, (cuqi.implicitprior.RegularizedGaussian, cuqi.implicitprior.RegularizedGMRF)):
213
+ raise TypeError("Prior needs to be RegularizedGaussian or RegularizedGMRF")
214
+ if not callable(self.proximal):
215
+ raise TypeError("Proximal needs to be callable")
243
216
 
244
217
  def _choose_stepsize(self):
245
218
  if isinstance(self.stepsize, str):