CUQIpy 1.0.0.post0.dev57__py3-none-any.whl → 1.0.0.post0.dev91__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of CUQIpy might be problematic. Click here for more details.
- {CUQIpy-1.0.0.post0.dev57.dist-info → CUQIpy-1.0.0.post0.dev91.dist-info}/METADATA +2 -2
- {CUQIpy-1.0.0.post0.dev57.dist-info → CUQIpy-1.0.0.post0.dev91.dist-info}/RECORD +9 -8
- cuqi/_version.py +3 -3
- cuqi/experimental/mcmc/__init__.py +1 -0
- cuqi/experimental/mcmc/_cwmh.py +221 -0
- cuqi/samples/_samples.py +10 -3
- {CUQIpy-1.0.0.post0.dev57.dist-info → CUQIpy-1.0.0.post0.dev91.dist-info}/LICENSE +0 -0
- {CUQIpy-1.0.0.post0.dev57.dist-info → CUQIpy-1.0.0.post0.dev91.dist-info}/WHEEL +0 -0
- {CUQIpy-1.0.0.post0.dev57.dist-info → CUQIpy-1.0.0.post0.dev91.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: CUQIpy
|
|
3
|
-
Version: 1.0.0.post0.
|
|
3
|
+
Version: 1.0.0.post0.dev91
|
|
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
|
|
@@ -202,7 +202,7 @@ Description-Content-Type: text/markdown
|
|
|
202
202
|
License-File: LICENSE
|
|
203
203
|
Requires-Dist: matplotlib
|
|
204
204
|
Requires-Dist: numpy >=1.17.0
|
|
205
|
-
Requires-Dist: scipy
|
|
205
|
+
Requires-Dist: scipy <1.13
|
|
206
206
|
Requires-Dist: arviz
|
|
207
207
|
|
|
208
208
|
<div align="center">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
cuqi/__init__.py,sha256=LsGilhl-hBLEn6Glt8S_l0OJzAA1sKit_rui8h-D-p0,488
|
|
2
2
|
cuqi/_messages.py,sha256=fzEBrZT2kbmfecBBPm7spVu7yHdxGARQB4QzXhJbCJ0,415
|
|
3
|
-
cuqi/_version.py,sha256=
|
|
3
|
+
cuqi/_version.py,sha256=wjkb-AlAnGV3_a5XYx4H4xHteXak-c9qRWEs8JAhr3k,509
|
|
4
4
|
cuqi/config.py,sha256=wcYvz19wkeKW2EKCGIKJiTpWt5kdaxyt4imyRkvtTRA,526
|
|
5
5
|
cuqi/diagnostics.py,sha256=5OrbJeqpynqRXOe5MtOKKhe7EAVdOEpHIqHnlMW9G_c,3029
|
|
6
6
|
cuqi/array/__init__.py,sha256=-EeiaiWGNsE3twRS4dD814BIlfxEsNkTCZUc5gjOXb0,30
|
|
@@ -32,7 +32,8 @@ cuqi/distribution/_normal.py,sha256=UeoTtGDT7YSf4ZNo2amlVF9K-YQpYbf8q76jcRJTVFw,
|
|
|
32
32
|
cuqi/distribution/_posterior.py,sha256=zAfL0GECxekZ2lBt1W6_LN0U_xskMwK4VNce5xAF7ig,5018
|
|
33
33
|
cuqi/distribution/_uniform.py,sha256=7xJmCZH_LPhuGkwEDGh-_CTtzcWKrXMOxtTJUFb7Ydo,1607
|
|
34
34
|
cuqi/experimental/__init__.py,sha256=vhZvyMX6rl8Y0haqCzGLPz6PSUKyu75XMQbeDHqTTrw,83
|
|
35
|
-
cuqi/experimental/mcmc/__init__.py,sha256=
|
|
35
|
+
cuqi/experimental/mcmc/__init__.py,sha256=MV1lHnRJTBsJMYEywnEx4iTEEAvN1SLIXhpRU_eiX-8,258
|
|
36
|
+
cuqi/experimental/mcmc/_cwmh.py,sha256=G-8YjMqPraZm1Pm3n6scFkpa65gdtI1WTQxlL21etEI,8066
|
|
36
37
|
cuqi/experimental/mcmc/_langevin_algorithm.py,sha256=ckVHDXLaw8hsUaOAFAEs7bL2Ny7W1QBKSc4AAC-TCis,9986
|
|
37
38
|
cuqi/experimental/mcmc/_mh.py,sha256=AslackZJ3hPUNQfy70Fh9WoRPtcsHGqulI0LQgGHBus,3477
|
|
38
39
|
cuqi/experimental/mcmc/_pcn.py,sha256=ma3pFqFgOmE7woZ41B5CGccKEuaacJPTmKvSEQhvtzs,3981
|
|
@@ -65,7 +66,7 @@ cuqi/sampler/_pcn.py,sha256=F0h9-nUFtkqn-o-1s8BCsmr8V7u6R7ycoCOeeV1uhj0,8601
|
|
|
65
66
|
cuqi/sampler/_rto.py,sha256=-AtMiYq4fh7pF9zVqfYjYtQbIIEGayrWyRGTj8KecfE,11518
|
|
66
67
|
cuqi/sampler/_sampler.py,sha256=TkZ_WAS-5Q43oICa-Elc2gftsRTBd7PEDUMDZ9tTGmU,5712
|
|
67
68
|
cuqi/samples/__init__.py,sha256=E7B9IBUsiOBr-HAKH9o3_Lqhq4KeWO87hmesHkdbwTY,30
|
|
68
|
-
cuqi/samples/_samples.py,sha256=
|
|
69
|
+
cuqi/samples/_samples.py,sha256=UUyAhADdkshZZ-wi1ICl3u7FYx1OU5NXgO-EeAS5TCc,34804
|
|
69
70
|
cuqi/solver/__init__.py,sha256=DGl8IdUnochRXHNDEy_13o_VT0vLFY6FjMmmSH6YUkY,169
|
|
70
71
|
cuqi/solver/_solver.py,sha256=TgezixCVf8nKGtEF9ZrkaTtAfxSs1Z8CR_cmhdTMqRw,22776
|
|
71
72
|
cuqi/testproblem/__init__.py,sha256=DWTOcyuNHMbhEuuWlY5CkYkNDSAqhvsKmJXBLivyblU,202
|
|
@@ -73,8 +74,8 @@ cuqi/testproblem/_testproblem.py,sha256=x769LwwRdJdzIiZkcQUGb_5-vynNTNALXWKato7s
|
|
|
73
74
|
cuqi/utilities/__init__.py,sha256=EfxHLdsyDNugbmbzs43nV_AeKcycM9sVBjG9WZydagA,351
|
|
74
75
|
cuqi/utilities/_get_python_variable_name.py,sha256=QwlBVj2koJRA8s8pWd554p7-ElcI7HUwY32HknaR92E,1827
|
|
75
76
|
cuqi/utilities/_utilities.py,sha256=At3DOXRdF3GwLkVcM2FXooGyjAGfPkIM0bRzhTfLmWk,8046
|
|
76
|
-
CUQIpy-1.0.0.post0.
|
|
77
|
-
CUQIpy-1.0.0.post0.
|
|
78
|
-
CUQIpy-1.0.0.post0.
|
|
79
|
-
CUQIpy-1.0.0.post0.
|
|
80
|
-
CUQIpy-1.0.0.post0.
|
|
77
|
+
CUQIpy-1.0.0.post0.dev91.dist-info/LICENSE,sha256=kJWRPrtRoQoZGXyyvu50Uc91X6_0XRaVfT0YZssicys,10799
|
|
78
|
+
CUQIpy-1.0.0.post0.dev91.dist-info/METADATA,sha256=KG8D1oXbdnsjU-xCHTLbwmzUCXkndGBNZ9-xGP6XguI,18392
|
|
79
|
+
CUQIpy-1.0.0.post0.dev91.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
80
|
+
CUQIpy-1.0.0.post0.dev91.dist-info/top_level.txt,sha256=AgmgMc6TKfPPqbjV0kvAoCBN334i_Lwwojc7HE3ZwD0,5
|
|
81
|
+
CUQIpy-1.0.0.post0.dev91.dist-info/RECORD,,
|
cuqi/_version.py
CHANGED
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2024-
|
|
11
|
+
"date": "2024-04-04T16:40:53+0200",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.0.0.post0.
|
|
14
|
+
"full-revisionid": "91a1edb3f1ae9412a27489f3faa9a4a9c8ff8a4a",
|
|
15
|
+
"version": "1.0.0.post0.dev91"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import cuqi
|
|
3
|
+
from cuqi.experimental.mcmc import ProposalBasedSamplerNew
|
|
4
|
+
from cuqi.array import CUQIarray
|
|
5
|
+
from numbers import Number
|
|
6
|
+
|
|
7
|
+
class CWMHNew(ProposalBasedSamplerNew):
|
|
8
|
+
"""Component-wise Metropolis Hastings sampler.
|
|
9
|
+
|
|
10
|
+
Allows sampling of a target distribution by a component-wise random-walk
|
|
11
|
+
sampling of a proposal distribution along with an accept/reject step.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
|
|
16
|
+
target : `cuqi.distribution.Distribution` or lambda function
|
|
17
|
+
The target distribution to sample. Custom logpdfs are supported by using
|
|
18
|
+
a :class:`cuqi.distribution.UserDefinedDistribution`.
|
|
19
|
+
|
|
20
|
+
proposal : `cuqi.distribution.Distribution` or callable method
|
|
21
|
+
The proposal to sample from. If a callable method it should provide a
|
|
22
|
+
single independent sample from proposal distribution. Defaults to a
|
|
23
|
+
Gaussian proposal. *Optional*.
|
|
24
|
+
|
|
25
|
+
scale : float or ndarray
|
|
26
|
+
Scale parameter used to define correlation between previous and proposed
|
|
27
|
+
sample in random-walk. *Optional*. If float, the same scale is used for
|
|
28
|
+
all dimensions. If ndarray, a (possibly) different scale is used for
|
|
29
|
+
each dimension.
|
|
30
|
+
|
|
31
|
+
initial_point : ndarray
|
|
32
|
+
Initial parameters. *Optional*
|
|
33
|
+
|
|
34
|
+
callback : callable, *Optional*
|
|
35
|
+
If set this function will be called after every sample.
|
|
36
|
+
The signature of the callback function is
|
|
37
|
+
`callback(sample, sample_index)`, where `sample` is the current sample
|
|
38
|
+
and `sample_index` is the index of the sample.
|
|
39
|
+
An example is shown in demos/demo31_callback.py.
|
|
40
|
+
|
|
41
|
+
kwargs : dict
|
|
42
|
+
Additional keyword arguments to be passed to the base class
|
|
43
|
+
:class:`ProposalBasedSamplerNew`.
|
|
44
|
+
|
|
45
|
+
Example
|
|
46
|
+
-------
|
|
47
|
+
.. code-block:: python
|
|
48
|
+
import numpy as np
|
|
49
|
+
import cuqi
|
|
50
|
+
# Parameters
|
|
51
|
+
dim = 5 # Dimension of distribution
|
|
52
|
+
mu = np.arange(dim) # Mean of Gaussian
|
|
53
|
+
std = 1 # standard deviation of Gaussian
|
|
54
|
+
|
|
55
|
+
# Logpdf function
|
|
56
|
+
logpdf_func = lambda x: -1/(std**2)*np.sum((x-mu)**2)
|
|
57
|
+
|
|
58
|
+
# Define distribution from logpdf as UserDefinedDistribution (sample
|
|
59
|
+
# and gradients also supported as inputs to UserDefinedDistribution)
|
|
60
|
+
target = cuqi.distribution.UserDefinedDistribution(
|
|
61
|
+
dim=dim, logpdf_func=logpdf_func)
|
|
62
|
+
|
|
63
|
+
# Set up sampler
|
|
64
|
+
sampler = cuqi.experimental.mcmc.CWMHNew(target, scale=1)
|
|
65
|
+
|
|
66
|
+
# Sample
|
|
67
|
+
samples = sampler.sample(2000).get_samples()
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
def __init__(self, target: cuqi.density.Density, proposal=None, scale=1,
|
|
71
|
+
initial_point=None, **kwargs):
|
|
72
|
+
super().__init__(target, proposal=proposal, scale=scale,
|
|
73
|
+
initial_point=initial_point, **kwargs)
|
|
74
|
+
|
|
75
|
+
# set initial scale
|
|
76
|
+
self.scale = scale
|
|
77
|
+
|
|
78
|
+
# set initial acceptance rate
|
|
79
|
+
self._acc = [np.ones((self.dim))]
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def scale(self):
|
|
83
|
+
""" Get the scale parameter. """
|
|
84
|
+
return self._scale
|
|
85
|
+
|
|
86
|
+
@scale.setter
|
|
87
|
+
def scale(self, value):
|
|
88
|
+
""" 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
|
+
|
|
95
|
+
def validate_target(self):
|
|
96
|
+
if not isinstance(self.target, cuqi.density.Density):
|
|
97
|
+
raise ValueError(
|
|
98
|
+
"Target should be an instance of "+\
|
|
99
|
+
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:
|
|
112
|
+
self._proposal = cuqi.distribution.Normal(
|
|
113
|
+
mean=lambda location: location,
|
|
114
|
+
std=lambda scale: scale,
|
|
115
|
+
geometry=self.dim,
|
|
116
|
+
)
|
|
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)
|
|
136
|
+
|
|
137
|
+
def step(self):
|
|
138
|
+
# Initialize x_t which is used to store the current CWMH sample
|
|
139
|
+
x_t = self.current_point.copy()
|
|
140
|
+
|
|
141
|
+
# Initialize x_star which is used to store the proposed sample by
|
|
142
|
+
# updating the current sample component-by-component
|
|
143
|
+
x_star = self.current_point.copy()
|
|
144
|
+
|
|
145
|
+
# Propose a sample x_all_components from the proposal distribution
|
|
146
|
+
# for all the components
|
|
147
|
+
target_eval_t = self.current_target
|
|
148
|
+
if isinstance(self.proposal,cuqi.distribution.Distribution):
|
|
149
|
+
x_all_components = self.proposal(
|
|
150
|
+
location= self.current_point, scale=self.scale).sample()
|
|
151
|
+
else:
|
|
152
|
+
x_all_components = self.proposal(self.current_point, self.scale)
|
|
153
|
+
|
|
154
|
+
# Initialize acceptance rate
|
|
155
|
+
acc = np.zeros(self.dim)
|
|
156
|
+
|
|
157
|
+
# Loop over all the components of the sample and accept/reject
|
|
158
|
+
# each component update.
|
|
159
|
+
for j in range(self.dim):
|
|
160
|
+
# propose state x_star by updating the j-th component
|
|
161
|
+
x_star[j] = x_all_components[j]
|
|
162
|
+
|
|
163
|
+
# evaluate target
|
|
164
|
+
target_eval_star = self.target.logd(x_star)
|
|
165
|
+
|
|
166
|
+
# compute Metropolis acceptance ratio
|
|
167
|
+
alpha = min(0, target_eval_star - target_eval_t)
|
|
168
|
+
|
|
169
|
+
# accept/reject
|
|
170
|
+
u_theta = np.log(np.random.rand())
|
|
171
|
+
if (u_theta <= alpha): # accept
|
|
172
|
+
x_t[j] = x_all_components[j]
|
|
173
|
+
target_eval_t = target_eval_star
|
|
174
|
+
acc[j] = 1
|
|
175
|
+
|
|
176
|
+
x_star = x_t.copy()
|
|
177
|
+
|
|
178
|
+
self.current_target = target_eval_t
|
|
179
|
+
self.current_point = x_t
|
|
180
|
+
|
|
181
|
+
return acc
|
|
182
|
+
|
|
183
|
+
def tune(self, skip_len, update_count):
|
|
184
|
+
# Store update_count in variable i for readability
|
|
185
|
+
i = update_count
|
|
186
|
+
|
|
187
|
+
# Optimal acceptance rate for CWMH
|
|
188
|
+
star_acc = 0.21/self.dim + 0.23
|
|
189
|
+
|
|
190
|
+
# Mean of acceptance rate over the last skip_len samples
|
|
191
|
+
hat_acc = np.mean(self._acc[i*skip_len:(i+1)*skip_len], axis=0)
|
|
192
|
+
|
|
193
|
+
# Compute new intermediate scaling parameter scale_temp
|
|
194
|
+
# Factor zeta ensures that the variation of the scale update vanishes
|
|
195
|
+
zeta = 1/np.sqrt(update_count+1)
|
|
196
|
+
scale_temp = np.exp(
|
|
197
|
+
np.log(self._scale_temp) + zeta*(hat_acc-star_acc))
|
|
198
|
+
|
|
199
|
+
# Update the scale parameter
|
|
200
|
+
self.scale = np.minimum(scale_temp, np.ones(self.dim))
|
|
201
|
+
self._scale_temp = scale_temp
|
|
202
|
+
|
|
203
|
+
def get_state(self):
|
|
204
|
+
current_point = self.current_point
|
|
205
|
+
if isinstance(current_point, CUQIarray):
|
|
206
|
+
current_point = current_point.to_numpy()
|
|
207
|
+
|
|
208
|
+
return {'sampler_type': 'CWMH',
|
|
209
|
+
'current_point': current_point,
|
|
210
|
+
'current_target': self.current_target,
|
|
211
|
+
'scale': self.scale}
|
|
212
|
+
|
|
213
|
+
def set_state(self, state):
|
|
214
|
+
current_point = state['current_point']
|
|
215
|
+
if not isinstance(current_point, CUQIarray):
|
|
216
|
+
current_point = CUQIarray(current_point,
|
|
217
|
+
geometry=self.target.geometry)
|
|
218
|
+
|
|
219
|
+
self.current_point = current_point
|
|
220
|
+
self.current_target = state['current_target']
|
|
221
|
+
self.scale = state['scale']
|
cuqi/samples/_samples.py
CHANGED
|
@@ -8,13 +8,20 @@ from copy import copy
|
|
|
8
8
|
from numbers import Number
|
|
9
9
|
|
|
10
10
|
try:
|
|
11
|
-
import arviz
|
|
12
|
-
except ImportError:
|
|
11
|
+
import arviz # Plotting tool
|
|
12
|
+
except ImportError as e:
|
|
13
13
|
arviz = None
|
|
14
|
+
arviz_import_error = e
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
def _check_for_arviz():
|
|
16
18
|
if arviz is None:
|
|
17
|
-
|
|
19
|
+
msg = "The arviz package is required for this functionality. "\
|
|
20
|
+
+ "Please make sure arviz is installed. "\
|
|
21
|
+
+ "See below for the original error message:\n"\
|
|
22
|
+
+ arviz_import_error.args[0]
|
|
23
|
+
|
|
24
|
+
raise ImportError(msg)
|
|
18
25
|
|
|
19
26
|
|
|
20
27
|
class Samples(object):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|