CUQIpy 1.1.1.post0.dev36__py3-none-any.whl → 1.4.1.post0.dev124__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.
- cuqi/__init__.py +2 -0
- cuqi/_version.py +3 -3
- cuqi/algebra/__init__.py +2 -0
- cuqi/algebra/_abstract_syntax_tree.py +358 -0
- cuqi/algebra/_ordered_set.py +82 -0
- cuqi/algebra/_random_variable.py +457 -0
- cuqi/array/_array.py +4 -13
- cuqi/config.py +7 -0
- cuqi/density/_density.py +9 -1
- cuqi/distribution/__init__.py +3 -2
- cuqi/distribution/_beta.py +7 -11
- cuqi/distribution/_cauchy.py +2 -2
- cuqi/distribution/_custom.py +0 -6
- cuqi/distribution/_distribution.py +31 -45
- cuqi/distribution/_gamma.py +7 -3
- cuqi/distribution/_gaussian.py +2 -12
- cuqi/distribution/_inverse_gamma.py +4 -10
- cuqi/distribution/_joint_distribution.py +112 -15
- cuqi/distribution/_lognormal.py +0 -7
- cuqi/distribution/{_modifiedhalfnormal.py → _modified_half_normal.py} +23 -23
- cuqi/distribution/_normal.py +34 -7
- cuqi/distribution/_posterior.py +9 -0
- cuqi/distribution/_truncated_normal.py +129 -0
- cuqi/distribution/_uniform.py +47 -1
- cuqi/experimental/__init__.py +2 -2
- cuqi/experimental/_recommender.py +216 -0
- cuqi/geometry/__init__.py +2 -0
- cuqi/geometry/_geometry.py +15 -1
- cuqi/geometry/_product_geometry.py +181 -0
- cuqi/implicitprior/__init__.py +5 -3
- cuqi/implicitprior/_regularized_gaussian.py +483 -0
- cuqi/implicitprior/{_regularizedGMRF.py → _regularized_gmrf.py} +4 -2
- cuqi/implicitprior/{_regularizedUnboundedUniform.py → _regularized_unbounded_uniform.py} +3 -2
- cuqi/implicitprior/_restorator.py +269 -0
- cuqi/legacy/__init__.py +2 -0
- cuqi/{experimental/mcmc → legacy/sampler}/__init__.py +7 -11
- cuqi/legacy/sampler/_conjugate.py +55 -0
- cuqi/legacy/sampler/_conjugate_approx.py +52 -0
- cuqi/legacy/sampler/_cwmh.py +196 -0
- cuqi/legacy/sampler/_gibbs.py +231 -0
- cuqi/legacy/sampler/_hmc.py +335 -0
- cuqi/{experimental/mcmc → legacy/sampler}/_langevin_algorithm.py +82 -111
- cuqi/legacy/sampler/_laplace_approximation.py +184 -0
- cuqi/legacy/sampler/_mh.py +190 -0
- cuqi/legacy/sampler/_pcn.py +244 -0
- cuqi/{experimental/mcmc → legacy/sampler}/_rto.py +132 -90
- cuqi/legacy/sampler/_sampler.py +182 -0
- cuqi/likelihood/_likelihood.py +9 -1
- cuqi/model/__init__.py +1 -1
- cuqi/model/_model.py +1361 -359
- cuqi/pde/__init__.py +4 -0
- cuqi/pde/_observation_map.py +36 -0
- cuqi/pde/_pde.py +134 -33
- cuqi/problem/_problem.py +93 -87
- cuqi/sampler/__init__.py +120 -8
- cuqi/sampler/_conjugate.py +376 -35
- cuqi/sampler/_conjugate_approx.py +40 -16
- cuqi/sampler/_cwmh.py +132 -138
- cuqi/{experimental/mcmc → sampler}/_direct.py +1 -1
- cuqi/sampler/_gibbs.py +288 -130
- cuqi/sampler/_hmc.py +328 -201
- cuqi/sampler/_langevin_algorithm.py +284 -100
- cuqi/sampler/_laplace_approximation.py +87 -117
- cuqi/sampler/_mh.py +47 -157
- cuqi/sampler/_pcn.py +65 -213
- cuqi/sampler/_rto.py +211 -142
- cuqi/sampler/_sampler.py +553 -136
- cuqi/samples/__init__.py +1 -1
- cuqi/samples/_samples.py +24 -18
- cuqi/solver/__init__.py +6 -4
- cuqi/solver/_solver.py +230 -26
- cuqi/testproblem/_testproblem.py +2 -3
- cuqi/utilities/__init__.py +6 -1
- cuqi/utilities/_get_python_variable_name.py +2 -2
- cuqi/utilities/_utilities.py +182 -2
- {CUQIpy-1.1.1.post0.dev36.dist-info → cuqipy-1.4.1.post0.dev124.dist-info}/METADATA +10 -6
- cuqipy-1.4.1.post0.dev124.dist-info/RECORD +101 -0
- {CUQIpy-1.1.1.post0.dev36.dist-info → cuqipy-1.4.1.post0.dev124.dist-info}/WHEEL +1 -1
- CUQIpy-1.1.1.post0.dev36.dist-info/RECORD +0 -92
- cuqi/experimental/mcmc/_conjugate.py +0 -197
- cuqi/experimental/mcmc/_conjugate_approx.py +0 -81
- cuqi/experimental/mcmc/_cwmh.py +0 -191
- cuqi/experimental/mcmc/_gibbs.py +0 -268
- cuqi/experimental/mcmc/_hmc.py +0 -470
- cuqi/experimental/mcmc/_laplace_approximation.py +0 -156
- cuqi/experimental/mcmc/_mh.py +0 -78
- cuqi/experimental/mcmc/_pcn.py +0 -89
- cuqi/experimental/mcmc/_sampler.py +0 -561
- cuqi/experimental/mcmc/_utilities.py +0 -17
- cuqi/implicitprior/_regularizedGaussian.py +0 -323
- {CUQIpy-1.1.1.post0.dev36.dist-info → cuqipy-1.4.1.post0.dev124.dist-info/licenses}/LICENSE +0 -0
- {CUQIpy-1.1.1.post0.dev36.dist-info → cuqipy-1.4.1.post0.dev124.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
import sys
|
|
3
|
+
import numpy as np
|
|
4
|
+
import cuqi
|
|
5
|
+
from cuqi.samples import Samples
|
|
6
|
+
import warnings
|
|
7
|
+
|
|
8
|
+
class Sampler(ABC):
|
|
9
|
+
|
|
10
|
+
def __init__(self, target, x0=None, dim=None, callback=None):
|
|
11
|
+
|
|
12
|
+
warnings.warn(f"\nYou are using the legacy sampler '{self.__class__.__name__}'.\n"
|
|
13
|
+
f"This will be removed in a future release of CUQIpy.\n"
|
|
14
|
+
f"Please consider using the new samplers in the 'cuqi.sampler' module.\n", UserWarning, stacklevel=2)
|
|
15
|
+
|
|
16
|
+
self._dim = dim
|
|
17
|
+
if hasattr(target,'dim'):
|
|
18
|
+
if self._dim is None:
|
|
19
|
+
self._dim = target.dim
|
|
20
|
+
elif self._dim != target.dim:
|
|
21
|
+
raise ValueError("'dim' need to be None or equal to 'target.dim'")
|
|
22
|
+
elif x0 is not None:
|
|
23
|
+
self._dim = len(x0)
|
|
24
|
+
|
|
25
|
+
self.target = target
|
|
26
|
+
|
|
27
|
+
if x0 is None:
|
|
28
|
+
x0 = np.ones(self.dim)
|
|
29
|
+
self.x0 = x0
|
|
30
|
+
|
|
31
|
+
self.callback = callback
|
|
32
|
+
|
|
33
|
+
def step(self, x):
|
|
34
|
+
"""
|
|
35
|
+
Perform a single MCMC step
|
|
36
|
+
"""
|
|
37
|
+
# Currently a hack to get step method for any sampler
|
|
38
|
+
self.x0 = x
|
|
39
|
+
return self.sample(2).samples[:,-1]
|
|
40
|
+
|
|
41
|
+
def step_tune(self, x, *args, **kwargs):
|
|
42
|
+
"""
|
|
43
|
+
Perform a single MCMC step and tune the sampler. This is used during burn-in.
|
|
44
|
+
"""
|
|
45
|
+
# Currently a hack to get step method for any sampler
|
|
46
|
+
out = self.step(x)
|
|
47
|
+
self.tune(*args, *kwargs)
|
|
48
|
+
return out
|
|
49
|
+
|
|
50
|
+
def tune(self):
|
|
51
|
+
"""
|
|
52
|
+
Tune the sampler parameters.
|
|
53
|
+
"""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def geometry(self):
|
|
59
|
+
if hasattr(self, 'target') and hasattr(self.target, 'geometry'):
|
|
60
|
+
geom = self.target.geometry
|
|
61
|
+
else:
|
|
62
|
+
geom = cuqi.geometry._DefaultGeometry1D(self.dim)
|
|
63
|
+
return geom
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def target(self):
|
|
67
|
+
return self._target
|
|
68
|
+
|
|
69
|
+
@target.setter
|
|
70
|
+
def target(self, value):
|
|
71
|
+
if not isinstance(value, cuqi.distribution.Distribution) and callable(value):
|
|
72
|
+
# obtain self.dim
|
|
73
|
+
if self.dim is not None:
|
|
74
|
+
dim = self.dim
|
|
75
|
+
else:
|
|
76
|
+
raise ValueError(f"If 'target' is a lambda function, the parameter 'dim' need to be specified when initializing {self.__class__}.")
|
|
77
|
+
|
|
78
|
+
# set target
|
|
79
|
+
self._target = cuqi.distribution.UserDefinedDistribution(logpdf_func=value, dim = dim)
|
|
80
|
+
|
|
81
|
+
elif isinstance(value, cuqi.distribution.Distribution):
|
|
82
|
+
self._target = value
|
|
83
|
+
else:
|
|
84
|
+
raise ValueError("'target' need to be either a lambda function or of type 'cuqi.distribution.Distribution'")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def dim(self):
|
|
89
|
+
if hasattr(self,'target') and hasattr(self.target,'dim'):
|
|
90
|
+
self._dim = self.target.dim
|
|
91
|
+
return self._dim
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def sample(self,N,Nb=0):
|
|
95
|
+
# Get samples from the samplers sample method
|
|
96
|
+
result = self._sample(N,Nb)
|
|
97
|
+
return self._create_Sample_object(result,N+Nb)
|
|
98
|
+
|
|
99
|
+
def sample_adapt(self,N,Nb=0):
|
|
100
|
+
# Get samples from the samplers sample method
|
|
101
|
+
result = self._sample_adapt(N,Nb)
|
|
102
|
+
return self._create_Sample_object(result,N+Nb)
|
|
103
|
+
|
|
104
|
+
def _create_Sample_object(self,result,N):
|
|
105
|
+
loglike_eval = None
|
|
106
|
+
acc_rate = None
|
|
107
|
+
if isinstance(result,tuple):
|
|
108
|
+
#Unpack samples+loglike+acc_rate
|
|
109
|
+
s = result[0]
|
|
110
|
+
if len(result)>1: loglike_eval = result[1]
|
|
111
|
+
if len(result)>2: acc_rate = result[2]
|
|
112
|
+
if len(result)>3: raise TypeError("Expected tuple of at most 3 elements from sampling method.")
|
|
113
|
+
else:
|
|
114
|
+
s = result
|
|
115
|
+
|
|
116
|
+
#Store samples in cuqi samples object if more than 1 sample
|
|
117
|
+
if N==1:
|
|
118
|
+
if len(s) == 1 and isinstance(s,np.ndarray): #Extract single value from numpy array
|
|
119
|
+
s = s.ravel()[0]
|
|
120
|
+
else:
|
|
121
|
+
s = s.flatten()
|
|
122
|
+
else:
|
|
123
|
+
s = Samples(s, self.geometry)#, geometry = self.geometry)
|
|
124
|
+
s.loglike_eval = loglike_eval
|
|
125
|
+
s.acc_rate = acc_rate
|
|
126
|
+
return s
|
|
127
|
+
|
|
128
|
+
@abstractmethod
|
|
129
|
+
def _sample(self,N,Nb):
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
@abstractmethod
|
|
133
|
+
def _sample_adapt(self,N,Nb):
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
def _print_progress(self,s,Ns):
|
|
137
|
+
"""Prints sampling progress"""
|
|
138
|
+
if Ns > 2:
|
|
139
|
+
if (s % (max(Ns//100,1))) == 0:
|
|
140
|
+
msg = f'Sample {s} / {Ns}'
|
|
141
|
+
sys.stdout.write('\r'+msg)
|
|
142
|
+
if s==Ns:
|
|
143
|
+
msg = f'Sample {s} / {Ns}'
|
|
144
|
+
sys.stdout.write('\r'+msg+'\n')
|
|
145
|
+
|
|
146
|
+
def _call_callback(self, sample, sample_index):
|
|
147
|
+
""" Calls the callback function. Assumes input is sample and sample index"""
|
|
148
|
+
if self.callback is not None:
|
|
149
|
+
self.callback(sample, sample_index)
|
|
150
|
+
|
|
151
|
+
class ProposalBasedSampler(Sampler,ABC):
|
|
152
|
+
def __init__(self, target, proposal=None, scale=1, x0=None, dim=None, **kwargs):
|
|
153
|
+
#TODO: after fixing None dim
|
|
154
|
+
#if dim is None and hasattr(proposal,'dim'):
|
|
155
|
+
# dim = proposal.dim
|
|
156
|
+
super().__init__(target, x0=x0, dim=dim, **kwargs)
|
|
157
|
+
|
|
158
|
+
self.proposal =proposal
|
|
159
|
+
self.scale = scale
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def proposal(self):
|
|
164
|
+
return self._proposal
|
|
165
|
+
|
|
166
|
+
@proposal.setter
|
|
167
|
+
def proposal(self, value):
|
|
168
|
+
self._proposal = value
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def geometry(self):
|
|
172
|
+
geom1, geom2 = None, None
|
|
173
|
+
if hasattr(self, 'proposal') and hasattr(self.proposal, 'geometry') and self.proposal.geometry.par_dim is not None:
|
|
174
|
+
geom1= self.proposal.geometry
|
|
175
|
+
if hasattr(self, 'target') and hasattr(self.target, 'geometry') and self.target.geometry.par_dim is not None:
|
|
176
|
+
geom2 = self.target.geometry
|
|
177
|
+
if not isinstance(geom1,cuqi.geometry._DefaultGeometry) and geom1 is not None:
|
|
178
|
+
return geom1
|
|
179
|
+
elif not isinstance(geom2,cuqi.geometry._DefaultGeometry) and geom2 is not None:
|
|
180
|
+
return geom2
|
|
181
|
+
else:
|
|
182
|
+
return cuqi.geometry._DefaultGeometry1D(self.dim)
|
cuqi/likelihood/_likelihood.py
CHANGED
|
@@ -43,6 +43,14 @@ class Likelihood(Density):
|
|
|
43
43
|
def name(self, value):
|
|
44
44
|
self.distribution.name = value
|
|
45
45
|
|
|
46
|
+
@property
|
|
47
|
+
def _name(self):
|
|
48
|
+
return self.distribution._name
|
|
49
|
+
|
|
50
|
+
@_name.setter
|
|
51
|
+
def _name(self, value):
|
|
52
|
+
self.distribution._name = value
|
|
53
|
+
|
|
46
54
|
@property
|
|
47
55
|
def FD_enabled(self):
|
|
48
56
|
""" Return FD_enabled of the likelihood from the underlying distribution """
|
|
@@ -204,4 +212,4 @@ class UserDefinedLikelihood(object):
|
|
|
204
212
|
return get_non_default_args(self.logpdf_func)
|
|
205
213
|
|
|
206
214
|
def __repr__(self) -> str:
|
|
207
|
-
return "CUQI {} function. Parameters {}.".format(self.__class__.__name__,self.get_parameter_names())
|
|
215
|
+
return "CUQI {} function. Parameters {}.".format(self.__class__.__name__,self.get_parameter_names())
|
cuqi/model/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
from ._model import Model, LinearModel, PDEModel
|
|
1
|
+
from ._model import Model, LinearModel, PDEModel, AffineModel
|