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,483 @@
|
|
|
1
|
+
from cuqi.utilities import get_non_default_args
|
|
2
|
+
from cuqi.distribution import Distribution, Gaussian
|
|
3
|
+
from cuqi.solver import ProjectNonnegative, ProjectBox, ProximalL1
|
|
4
|
+
from cuqi.geometry import Continuous1D, Continuous2D, Image2D
|
|
5
|
+
from cuqi.operator import FirstOrderFiniteDifference, SecondOrderFiniteDifference, Operator
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import scipy.sparse as sparse
|
|
9
|
+
import scipy.optimize as spoptimize
|
|
10
|
+
|
|
11
|
+
from copy import copy
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RegularizedGaussian(Distribution):
|
|
15
|
+
""" Implicit Regularized Gaussian.
|
|
16
|
+
|
|
17
|
+
Defines a so-called implicit prior based on a Gaussian distribution with implicit regularization.
|
|
18
|
+
The regularization can be defined in the form of a proximal operator or a projector.
|
|
19
|
+
Alternatively, preset constraints and regularization can be used.
|
|
20
|
+
|
|
21
|
+
Precisely one of proximal, projector, constraint or regularization needs to be provided. Otherwise, an error is raised.
|
|
22
|
+
|
|
23
|
+
Can be used as a prior in a posterior which can be sampled with the RegularizedLinearRTO sampler.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
For more details on implicit regularized Gaussian see the following paper:
|
|
27
|
+
|
|
28
|
+
[1] Everink, Jasper M., Yiqiu Dong, and Martin S. Andersen. "Sparse Bayesian inference with regularized
|
|
29
|
+
Gaussian distributions." Inverse Problems 39.11 (2023): 115004.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
mean
|
|
34
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
35
|
+
|
|
36
|
+
cov
|
|
37
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
38
|
+
|
|
39
|
+
prec
|
|
40
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
41
|
+
|
|
42
|
+
sqrtcov
|
|
43
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
44
|
+
|
|
45
|
+
sqrtprec
|
|
46
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
47
|
+
|
|
48
|
+
proximal : callable f(x, scale), list of tuples (callable proximal operator of f_i, linear operator L_i) or None
|
|
49
|
+
If callable:
|
|
50
|
+
Euclidean proximal operator f of the regularization function g, that is, a solver for the optimization problem
|
|
51
|
+
min_z 0.5||x-z||_2^2+scale*g(x).
|
|
52
|
+
If list of tuples (callable proximal operator of f_i, linear operator L_i):
|
|
53
|
+
Each callable proximal operator of f_i accepts two arguments (x, p) and should return the minimizer of p/2||x-z||^2 + f(x) over z for some f.
|
|
54
|
+
Each linear operator needs to have the '__matmul__', 'T' and 'shape' attributes;
|
|
55
|
+
this includes numpy.ndarray, scipy.sparse.sparray, scipy.sparse.linalg.LinearOperator and cuqi.operator.Operator.
|
|
56
|
+
The corresponding regularization takes the form
|
|
57
|
+
sum_i f_i(L_i x),
|
|
58
|
+
where the sum ranges from 1 to an arbitrary n.
|
|
59
|
+
|
|
60
|
+
projector : callable f(x) or None
|
|
61
|
+
Euclidean projection onto the constraint C, that is, a solver for the optimization problem
|
|
62
|
+
min_(z in C) 0.5||x-z||_2^2.
|
|
63
|
+
|
|
64
|
+
constraint : string or None
|
|
65
|
+
Preset constraints that generate the corresponding proximal parameter. Required for use in Gibbs. For any geometry the following can be chosen:
|
|
66
|
+
- "nonnegativity"
|
|
67
|
+
- "box", the following additional parameters can be passed:
|
|
68
|
+
lower_bound : array_like or None
|
|
69
|
+
Lower bound of box, defaults to zero
|
|
70
|
+
upper_bound : array_like
|
|
71
|
+
Upper bound of box, defaults to one
|
|
72
|
+
Additionally, for Continuous1D geometry the following options can be chosen:
|
|
73
|
+
- "increasing"
|
|
74
|
+
- "decreasing"
|
|
75
|
+
- "convex"
|
|
76
|
+
- "concave"
|
|
77
|
+
|
|
78
|
+
regularization : string or None
|
|
79
|
+
Preset regularization that generate the corresponding proximal parameter. Can be set to "l1" or 'tv'. Required for use in Gibbs in future update.
|
|
80
|
+
For "l1" or "tv", the following additional parameters can be passed:
|
|
81
|
+
strength : scalar
|
|
82
|
+
Regularization parameter, i.e., strength*||Lx||_1, defaults to one
|
|
83
|
+
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(self, mean=None, cov=None, prec=None, sqrtcov=None, sqrtprec=None, proximal = None, projector = None, constraint = None, regularization = None, **kwargs):
|
|
87
|
+
|
|
88
|
+
# Store regularization parameters and remove them from kwargs passed to Gaussian
|
|
89
|
+
optional_regularization_parameters = {
|
|
90
|
+
"lower_bound" : kwargs.pop("lower_bound", None), # Takes default of ProjectBox if None
|
|
91
|
+
"upper_bound" : kwargs.pop("upper_bound", None), # Takes default of ProjectBox if None
|
|
92
|
+
"strength" : kwargs.pop("strength", 1)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# We init the underlying Gaussian first for geometry and dimensionality handling
|
|
96
|
+
self._gaussian = Gaussian(mean=mean, cov=cov, prec=prec, sqrtcov=sqrtcov, sqrtprec=sqrtprec, **kwargs)
|
|
97
|
+
kwargs.pop("geometry", None)
|
|
98
|
+
|
|
99
|
+
# Init from abstract distribution class
|
|
100
|
+
super().__init__(**kwargs)
|
|
101
|
+
|
|
102
|
+
self._force_list = False
|
|
103
|
+
self._parse_regularization_input_arguments(proximal, projector, constraint, regularization, optional_regularization_parameters)
|
|
104
|
+
|
|
105
|
+
def _parse_regularization_input_arguments(self, proximal, projector, constraint, regularization, optional_regularization_parameters):
|
|
106
|
+
""" Parse regularization input arguments with guarding statements and store internal states """
|
|
107
|
+
|
|
108
|
+
# Guards checking whether the regularization inputs are valid
|
|
109
|
+
if (proximal is not None) + (projector is not None) + max((constraint is not None), (regularization is not None)) == 0:
|
|
110
|
+
raise ValueError("At least some constraint or regularization has to be specified for RegularizedGaussian")
|
|
111
|
+
|
|
112
|
+
if (proximal is not None) + (projector is not None) == 2:
|
|
113
|
+
raise ValueError("Only one of proximal or projector can be used.")
|
|
114
|
+
|
|
115
|
+
if (proximal is not None) + (projector is not None) + max((constraint is not None), (regularization is not None)) > 1:
|
|
116
|
+
raise ValueError("User-defined proximals and projectors cannot be combined with pre-defined constraints and regularization.")
|
|
117
|
+
|
|
118
|
+
# Branch between user-defined and preset
|
|
119
|
+
if (proximal is not None) + (projector is not None) >= 1:
|
|
120
|
+
self._parse_user_specified_input(proximal, projector)
|
|
121
|
+
else:
|
|
122
|
+
# Set constraint and regularization presets for use with Gibbs
|
|
123
|
+
self._preset = {"constraint": None,
|
|
124
|
+
"regularization": None}
|
|
125
|
+
|
|
126
|
+
self._parse_preset_constraint_input(constraint, optional_regularization_parameters)
|
|
127
|
+
self._parse_preset_regularization_input(regularization, optional_regularization_parameters)
|
|
128
|
+
|
|
129
|
+
# Merge
|
|
130
|
+
self._merge_predefined_option()
|
|
131
|
+
|
|
132
|
+
def _parse_user_specified_input(self, proximal, projector):
|
|
133
|
+
# Guard for checking partial validy of proximals or projectors
|
|
134
|
+
if proximal is not None:
|
|
135
|
+
if callable(proximal):
|
|
136
|
+
if len(get_non_default_args(proximal)) != 2:
|
|
137
|
+
raise ValueError("Proximal should take 2 arguments.")
|
|
138
|
+
elif isinstance(proximal, list):
|
|
139
|
+
for val in proximal:
|
|
140
|
+
if len(val) != 2:
|
|
141
|
+
raise ValueError("Each value in the proximal list needs to consistent of two elements: a proximal operator and a linear operator.")
|
|
142
|
+
if callable(val[0]):
|
|
143
|
+
if len(get_non_default_args(val[0])) != 2:
|
|
144
|
+
raise ValueError("Proximal should take 2 arguments.")
|
|
145
|
+
else:
|
|
146
|
+
raise ValueError("Proximal operators need to be callable.")
|
|
147
|
+
if not (hasattr(val[1], '__matmul__') and hasattr(val[1], 'T') and hasattr(val[1], 'shape')):
|
|
148
|
+
raise ValueError("Linear operator not supported, must have '__matmul__', 'T' and 'shape' attributes.")
|
|
149
|
+
else:
|
|
150
|
+
raise ValueError("Proximal needs to be callable or a list. See documentation.")
|
|
151
|
+
|
|
152
|
+
if projector is not None:
|
|
153
|
+
if callable(projector):
|
|
154
|
+
if len(get_non_default_args(projector)) != 1:
|
|
155
|
+
raise ValueError("Projector should take 1 argument.")
|
|
156
|
+
else:
|
|
157
|
+
raise ValueError("Projector needs to be callable")
|
|
158
|
+
|
|
159
|
+
# Set user-defined proximals or projectors
|
|
160
|
+
if proximal is not None:
|
|
161
|
+
self._preset = None
|
|
162
|
+
self._proximal = proximal
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
if projector is not None:
|
|
166
|
+
self._preset = None
|
|
167
|
+
self._proximal = lambda z, gamma: projector(z)
|
|
168
|
+
return
|
|
169
|
+
|
|
170
|
+
def _parse_preset_constraint_input(self, constraint, optional_regularization_parameters):
|
|
171
|
+
# Create data for constraints
|
|
172
|
+
self._constraint_prox = None
|
|
173
|
+
self._constraint_oper = None
|
|
174
|
+
if constraint is not None:
|
|
175
|
+
if not isinstance(constraint, str):
|
|
176
|
+
raise ValueError("Constraint needs to be specified as a string.")
|
|
177
|
+
|
|
178
|
+
c_lower = constraint.lower()
|
|
179
|
+
if c_lower == "nonnegativity":
|
|
180
|
+
self._constraint_prox = lambda z, gamma: ProjectNonnegative(z)
|
|
181
|
+
self._box_bounds = (np.ones(self.dim)*0, np.ones(self.dim)*np.inf)
|
|
182
|
+
self._preset["constraint"] = "nonnegativity"
|
|
183
|
+
elif c_lower == "box":
|
|
184
|
+
_box_lower = optional_regularization_parameters["lower_bound"]
|
|
185
|
+
_box_upper = optional_regularization_parameters["upper_bound"]
|
|
186
|
+
self._proximal = lambda z, _: ProjectBox(z, _box_lower, _box_upper)
|
|
187
|
+
self._box_bounds = (np.ones(self.dim)*_box_lower, np.ones(self.dim)*_box_upper)
|
|
188
|
+
self._preset["constraint"] = "box"
|
|
189
|
+
elif c_lower == "increasing":
|
|
190
|
+
if not isinstance(self.geometry, Continuous1D):
|
|
191
|
+
raise ValueError("Geometry not supported for " + c_lower)
|
|
192
|
+
if hasattr(spoptimize, 'isotonic_regression'):
|
|
193
|
+
self._constraint_prox = lambda z, _: spoptimize.isotonic_regression(z, increasing=True).x
|
|
194
|
+
else:
|
|
195
|
+
raise AttributeError(f"The function 'isotonic_regression' does not exist in scipy.optimize. Installed scipy version: {spoptimize.__version__}. You need to install a scipy >= 1.12.0")
|
|
196
|
+
self._preset["constraint"] = "increasing"
|
|
197
|
+
elif c_lower == "decreasing":
|
|
198
|
+
if not isinstance(self.geometry, Continuous1D):
|
|
199
|
+
raise ValueError("Geometry not supported for " + c_lower)
|
|
200
|
+
if hasattr(spoptimize, 'isotonic_regression'):
|
|
201
|
+
self._constraint_prox = lambda z, _: spoptimize.isotonic_regression(z, increasing=False).x
|
|
202
|
+
else:
|
|
203
|
+
raise AttributeError(f"The function 'isotonic_regression' does not exist in scipy.optimize. Installed scipy version: {spoptimize.__version__}. You need to install a scipy >= 1.12.0")
|
|
204
|
+
self._preset["constraint"] = "decreasing"
|
|
205
|
+
elif c_lower == "convex":
|
|
206
|
+
if not isinstance(self.geometry, Continuous1D):
|
|
207
|
+
raise ValueError("Geometry not supported for " + c_lower)
|
|
208
|
+
self._constraint_prox = lambda z, _: -ProjectNonnegative(-z)
|
|
209
|
+
self._constraint_oper = SecondOrderFiniteDifference(self.geometry.fun_shape, bc_type='neumann')
|
|
210
|
+
self._preset["constraint"] = "convex"
|
|
211
|
+
elif c_lower == "concave":
|
|
212
|
+
if not isinstance(self.geometry, Continuous1D):
|
|
213
|
+
raise ValueError("Geometry not supported for " + c_lower)
|
|
214
|
+
self._constraint_prox = lambda z, _: ProjectNonnegative(z)
|
|
215
|
+
self._constraint_oper = SecondOrderFiniteDifference(self.geometry.fun_shape, bc_type='neumann')
|
|
216
|
+
self._preset["constraint"] = "concave"
|
|
217
|
+
else:
|
|
218
|
+
raise ValueError("Constraint not supported.")
|
|
219
|
+
|
|
220
|
+
def _parse_preset_regularization_input(self, regularization, optional_regularization_parameters):
|
|
221
|
+
# Create data for regularization
|
|
222
|
+
self._regularization_prox = None
|
|
223
|
+
self._regularization_oper = None
|
|
224
|
+
if regularization is not None:
|
|
225
|
+
if not isinstance(regularization, str):
|
|
226
|
+
raise ValueError("Regularization needs to be specified as a string.")
|
|
227
|
+
|
|
228
|
+
self._strength = optional_regularization_parameters["strength"]
|
|
229
|
+
r_lower = regularization.lower()
|
|
230
|
+
if r_lower == "l1":
|
|
231
|
+
self._regularization_prox = lambda z, gamma: ProximalL1(z, gamma*self._strength)
|
|
232
|
+
self._preset["regularization"] = "l1"
|
|
233
|
+
elif r_lower == "tv":
|
|
234
|
+
# Store the transformation to reuse when modifying the strength
|
|
235
|
+
if not isinstance(self.geometry, (Continuous1D, Continuous2D, Image2D)):
|
|
236
|
+
raise ValueError("Geometry not supported for total variation")
|
|
237
|
+
self._regularization_prox = lambda z, gamma: ProximalL1(z, gamma*self._strength)
|
|
238
|
+
self._regularization_oper = FirstOrderFiniteDifference(self.geometry.fun_shape, bc_type='neumann')
|
|
239
|
+
self._preset["regularization"] = "tv"
|
|
240
|
+
else:
|
|
241
|
+
raise ValueError("Regularization not supported.")
|
|
242
|
+
|
|
243
|
+
def _merge_predefined_option(self):
|
|
244
|
+
# Check whether it is a single proximal and hence FISTA could be used in RegularizedLinearRTO
|
|
245
|
+
if ((not self._force_list) and
|
|
246
|
+
((self._constraint_prox is not None) + (self._regularization_prox is not None) == 1) and
|
|
247
|
+
((self._constraint_oper is not None) + (self._regularization_oper is not None) == 0)):
|
|
248
|
+
if self._constraint_prox is not None:
|
|
249
|
+
self._proximal = self._constraint_prox
|
|
250
|
+
else:
|
|
251
|
+
self._proximal = self._regularization_prox
|
|
252
|
+
return
|
|
253
|
+
|
|
254
|
+
# Merge regularization choices in list for use in ADMM by RegularizedLinearRTO
|
|
255
|
+
self._proximal = []
|
|
256
|
+
if self._constraint_prox is not None:
|
|
257
|
+
self._proximal += [(self._constraint_prox, self._constraint_oper if self._constraint_oper is not None else sparse.eye(self.geometry.par_dim))]
|
|
258
|
+
if self._regularization_prox is not None:
|
|
259
|
+
self._proximal += [(self._regularization_prox, self._regularization_oper if self._regularization_oper is not None else sparse.eye(self.geometry.par_dim))]
|
|
260
|
+
|
|
261
|
+
@property
|
|
262
|
+
def transformation(self):
|
|
263
|
+
return self._transformation
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def strength(self):
|
|
267
|
+
return self._strength
|
|
268
|
+
|
|
269
|
+
@strength.setter
|
|
270
|
+
def strength(self, value):
|
|
271
|
+
if self._preset is None or self._preset["regularization"] is None:
|
|
272
|
+
raise TypeError("Strength is only used when the regularization is set to l1 or TV.")
|
|
273
|
+
|
|
274
|
+
self._strength = value
|
|
275
|
+
if self._preset["regularization"] in ["l1", "tv"]:
|
|
276
|
+
self._regularization_prox = lambda z, gamma: ProximalL1(z, gamma*self._strength)
|
|
277
|
+
|
|
278
|
+
# Create new list of proximals based on updated regularization
|
|
279
|
+
self._merge_predefined_option()
|
|
280
|
+
|
|
281
|
+
# This is a getter only attribute for the underlying Gaussian
|
|
282
|
+
# It also ensures that the name of the underlying Gaussian
|
|
283
|
+
# matches the name of the implicit regularized Gaussian
|
|
284
|
+
@property
|
|
285
|
+
def gaussian(self):
|
|
286
|
+
if self._name is not None:
|
|
287
|
+
self._gaussian._name = self._name
|
|
288
|
+
return self._gaussian
|
|
289
|
+
|
|
290
|
+
@property
|
|
291
|
+
def proximal(self):
|
|
292
|
+
return self._proximal
|
|
293
|
+
|
|
294
|
+
@proximal.setter
|
|
295
|
+
def proximal(self, value):
|
|
296
|
+
if callable(value):
|
|
297
|
+
if len(get_non_default_args(value)) != 2:
|
|
298
|
+
raise ValueError("Proximal should take 2 arguments.")
|
|
299
|
+
elif isinstance(value, list):
|
|
300
|
+
for (prox, op) in value:
|
|
301
|
+
if len(get_non_default_args(prox)) != 2:
|
|
302
|
+
raise ValueError("Proximal should take 2 arguments.")
|
|
303
|
+
if op.shape[1] != self.geometry.par_dim:
|
|
304
|
+
raise ValueError("Incorrect shape of linear operator in proximal list.")
|
|
305
|
+
else:
|
|
306
|
+
raise ValueError("Proximal needs to be callable or a list. See documentation.")
|
|
307
|
+
|
|
308
|
+
self._proximal = value
|
|
309
|
+
|
|
310
|
+
# For all the presets, self._proximal is set directly,
|
|
311
|
+
self._preset = None
|
|
312
|
+
|
|
313
|
+
@property
|
|
314
|
+
def preset(self):
|
|
315
|
+
return self._preset
|
|
316
|
+
|
|
317
|
+
def logpdf(self, x):
|
|
318
|
+
return np.nan
|
|
319
|
+
#raise ValueError(
|
|
320
|
+
# f"The logpdf of a implicit regularized Gaussian is not be defined.")
|
|
321
|
+
|
|
322
|
+
def _sample(self, N, rng=None):
|
|
323
|
+
raise ValueError(
|
|
324
|
+
"Cannot be sampled from.")
|
|
325
|
+
|
|
326
|
+
@staticmethod
|
|
327
|
+
def constraint_options():
|
|
328
|
+
return ["nonnegativity", "box", "increasing", "decreasing", "convex", "concave"]
|
|
329
|
+
|
|
330
|
+
@staticmethod
|
|
331
|
+
def regularization_options():
|
|
332
|
+
return ["l1", "tv"]
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# --- Defer behavior of the underlying Gaussian --- #
|
|
336
|
+
@property
|
|
337
|
+
def geometry(self):
|
|
338
|
+
return self.gaussian.geometry
|
|
339
|
+
|
|
340
|
+
@geometry.setter
|
|
341
|
+
def geometry(self, value):
|
|
342
|
+
self.gaussian.geometry = value
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def mean(self):
|
|
346
|
+
return self.gaussian.mean
|
|
347
|
+
|
|
348
|
+
@mean.setter
|
|
349
|
+
def mean(self, value):
|
|
350
|
+
self.gaussian.mean = value
|
|
351
|
+
|
|
352
|
+
@property
|
|
353
|
+
def cov(self):
|
|
354
|
+
return self.gaussian.cov
|
|
355
|
+
|
|
356
|
+
@cov.setter
|
|
357
|
+
def cov(self, value):
|
|
358
|
+
self.gaussian.cov = value
|
|
359
|
+
|
|
360
|
+
@property
|
|
361
|
+
def prec(self):
|
|
362
|
+
return self.gaussian.prec
|
|
363
|
+
|
|
364
|
+
@prec.setter
|
|
365
|
+
def prec(self, value):
|
|
366
|
+
self.gaussian.prec = value
|
|
367
|
+
|
|
368
|
+
@property
|
|
369
|
+
def sqrtprec(self):
|
|
370
|
+
return self.gaussian.sqrtprec
|
|
371
|
+
|
|
372
|
+
@sqrtprec.setter
|
|
373
|
+
def sqrtprec(self, value):
|
|
374
|
+
self.gaussian.sqrtprec = value
|
|
375
|
+
|
|
376
|
+
@property
|
|
377
|
+
def sqrtcov(self):
|
|
378
|
+
return self.gaussian.sqrtcov
|
|
379
|
+
|
|
380
|
+
@sqrtcov.setter
|
|
381
|
+
def sqrtcov(self, value):
|
|
382
|
+
self.gaussian.sqrtcov = value
|
|
383
|
+
|
|
384
|
+
def get_mutable_variables(self):
|
|
385
|
+
mutable_vars = self.gaussian.get_mutable_variables().copy()
|
|
386
|
+
if self.preset is not None and self.preset['regularization'] in ["l1", "tv"]:
|
|
387
|
+
mutable_vars += ["strength"]
|
|
388
|
+
return mutable_vars
|
|
389
|
+
|
|
390
|
+
def _make_copy(self):
|
|
391
|
+
""" Returns a shallow copy of the density keeping a pointer to the original. """
|
|
392
|
+
# Using deepcopy would also copy the underlying geometry, which causes a crash because geometries won't match anymore.
|
|
393
|
+
new_density = copy(self)
|
|
394
|
+
new_density._gaussian = copy(new_density._gaussian)
|
|
395
|
+
new_density._original_density = self
|
|
396
|
+
return new_density
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class ConstrainedGaussian(RegularizedGaussian):
|
|
400
|
+
""" Implicit Constrained Gaussian.
|
|
401
|
+
|
|
402
|
+
Defines a so-called implicit prior based on a Gaussian distribution with implicit constraints.
|
|
403
|
+
The constraint can be defined as a preset or in the form of a projector.
|
|
404
|
+
|
|
405
|
+
Precisely one of projector or constraint needs to be provided. Otherwise, an error is raised.
|
|
406
|
+
|
|
407
|
+
Can be used as a prior in a posterior which can be sampled with the RegularizedLinearRTO sampler.
|
|
408
|
+
|
|
409
|
+
Alias for :class:`~cuqi.implicitprior.RegularizedGaussian` with only constraints available.
|
|
410
|
+
|
|
411
|
+
For more details on implicit regularized Gaussian see the following paper:
|
|
412
|
+
|
|
413
|
+
[1] Everink, Jasper M., Yiqiu Dong, and Martin S. Andersen. "Sparse Bayesian inference with regularized
|
|
414
|
+
Gaussian distributions." Inverse Problems 39.11 (2023): 115004.
|
|
415
|
+
|
|
416
|
+
Parameters
|
|
417
|
+
----------
|
|
418
|
+
mean
|
|
419
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
420
|
+
|
|
421
|
+
cov
|
|
422
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
423
|
+
|
|
424
|
+
prec
|
|
425
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
426
|
+
|
|
427
|
+
sqrtcov
|
|
428
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
429
|
+
|
|
430
|
+
sqrtprec
|
|
431
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
432
|
+
|
|
433
|
+
projector : callable f(x) or None
|
|
434
|
+
Euclidean projection onto the constraint C, that is, a solver for the optimization problem
|
|
435
|
+
min_(z in C) 0.5||x-z||_2^2.
|
|
436
|
+
|
|
437
|
+
constraint : string or None
|
|
438
|
+
Preset constraints that generate the corresponding proximal parameter. Can be set to "nonnegativity" and "box". Required for use in Gibbs.
|
|
439
|
+
For "box", the following additional parameters can be passed:
|
|
440
|
+
lower_bound : array_like or None
|
|
441
|
+
Lower bound of box, defaults to zero
|
|
442
|
+
upper_bound : array_like
|
|
443
|
+
Upper bound of box, defaults to one
|
|
444
|
+
|
|
445
|
+
"""
|
|
446
|
+
def __init__(self, mean=None, cov=None, prec=None, sqrtcov=None,sqrtprec=None, projector=None, constraint=None, **kwargs):
|
|
447
|
+
super().__init__(mean=mean, cov=cov, prec=prec, sqrtcov=sqrtcov, sqrtprec=sqrtprec, projector=projector, constraint=constraint, **kwargs)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class NonnegativeGaussian(RegularizedGaussian):
|
|
451
|
+
""" Implicit Nonnegative Gaussian.
|
|
452
|
+
|
|
453
|
+
Defines a so-called implicit prior based on a Gaussian distribution with implicit nonnegativity constraints.
|
|
454
|
+
|
|
455
|
+
Can be used as a prior in a posterior which can be sampled with the RegularizedLinearRTO sampler.
|
|
456
|
+
|
|
457
|
+
Alias for :class:`~cuqi.implicitprior.RegularizedGaussian` with only nonnegativity constraints.
|
|
458
|
+
|
|
459
|
+
For more details on implicit regularized Gaussian see the following paper:
|
|
460
|
+
|
|
461
|
+
[1] Everink, Jasper M., Yiqiu Dong, and Martin S. Andersen. "Sparse Bayesian inference with regularized
|
|
462
|
+
Gaussian distributions." Inverse Problems 39.11 (2023): 115004.
|
|
463
|
+
|
|
464
|
+
Parameters
|
|
465
|
+
----------
|
|
466
|
+
mean
|
|
467
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
468
|
+
|
|
469
|
+
cov
|
|
470
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
471
|
+
|
|
472
|
+
prec
|
|
473
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
474
|
+
|
|
475
|
+
sqrtcov
|
|
476
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
477
|
+
|
|
478
|
+
sqrtprec
|
|
479
|
+
See :class:`~cuqi.distribution.Gaussian` for details.
|
|
480
|
+
|
|
481
|
+
"""
|
|
482
|
+
def __init__(self, mean=None, cov=None, prec=None, sqrtcov=None,sqrtprec=None, **kwargs):
|
|
483
|
+
super().__init__(mean=mean, cov=cov, prec=prec, sqrtcov=sqrtcov, sqrtprec=sqrtprec, constraint="nonnegativity", **kwargs)
|
|
@@ -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):
|
|
@@ -63,10 +63,12 @@ class RegularizedGMRF(RegularizedGaussian):
|
|
|
63
63
|
|
|
64
64
|
# Underlying explicit GMRF
|
|
65
65
|
self._gaussian = GMRF(mean, prec, bc_type=bc_type, order=order, **kwargs)
|
|
66
|
+
kwargs.pop("geometry", None)
|
|
66
67
|
|
|
67
68
|
# Init from abstract distribution class
|
|
68
69
|
super(Distribution, self).__init__(**kwargs)
|
|
69
70
|
|
|
71
|
+
self._force_list = False
|
|
70
72
|
self._parse_regularization_input_arguments(proximal, projector, constraint, regularization, args)
|
|
71
73
|
|
|
72
74
|
|
|
@@ -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):
|
|
@@ -63,4 +63,5 @@ class RegularizedUnboundedUniform(RegularizedGaussian):
|
|
|
63
63
|
# Init from abstract distribution class
|
|
64
64
|
super(Distribution, self).__init__(**kwargs)
|
|
65
65
|
|
|
66
|
+
self._force_list = False
|
|
66
67
|
self._parse_regularization_input_arguments(proximal, projector, constraint, regularization, args)
|