CUQIpy 1.3.0.post0.dev31__py3-none-any.whl → 1.3.0.post0.dev70__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/_version.py +3 -3
- cuqi/distribution/__init__.py +1 -1
- cuqi/distribution/_uniform.py +35 -1
- cuqi/experimental/mcmc/_conjugate.py +78 -27
- cuqi/implicitprior/_regularizedGaussian.py +34 -4
- cuqi/utilities/__init__.py +3 -1
- cuqi/utilities/_utilities.py +93 -11
- {cuqipy-1.3.0.post0.dev31.dist-info → cuqipy-1.3.0.post0.dev70.dist-info}/METADATA +3 -2
- {cuqipy-1.3.0.post0.dev31.dist-info → cuqipy-1.3.0.post0.dev70.dist-info}/RECORD +12 -12
- {cuqipy-1.3.0.post0.dev31.dist-info → cuqipy-1.3.0.post0.dev70.dist-info}/WHEEL +1 -1
- {cuqipy-1.3.0.post0.dev31.dist-info → cuqipy-1.3.0.post0.dev70.dist-info/licenses}/LICENSE +0 -0
- {cuqipy-1.3.0.post0.dev31.dist-info → cuqipy-1.3.0.post0.dev70.dist-info}/top_level.txt +0 -0
cuqi/_version.py
CHANGED
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2025-
|
|
11
|
+
"date": "2025-04-10T10:13:59+0200",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.3.0.post0.
|
|
14
|
+
"full-revisionid": "c18048b15c6ec1a66e8e4bc097e0180fc3d4e47e",
|
|
15
|
+
"version": "1.3.0.post0.dev70"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
cuqi/distribution/__init__.py
CHANGED
|
@@ -14,6 +14,6 @@ from ._lognormal import Lognormal
|
|
|
14
14
|
from ._normal import Normal
|
|
15
15
|
from ._truncated_normal import TruncatedNormal
|
|
16
16
|
from ._posterior import Posterior
|
|
17
|
-
from ._uniform import Uniform
|
|
17
|
+
from ._uniform import Uniform, UnboundedUniform
|
|
18
18
|
from ._custom import UserDefinedDistribution, DistributionGallery
|
|
19
19
|
from ._joint_distribution import JointDistribution, _StackedJointDistribution, MultipleLikelihoodPosterior
|
cuqi/distribution/_uniform.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
from cuqi.distribution import Distribution
|
|
3
|
+
from cuqi.geometry import Geometry
|
|
3
4
|
|
|
4
5
|
class Uniform(Distribution):
|
|
5
6
|
|
|
@@ -57,4 +58,37 @@ class Uniform(Distribution):
|
|
|
57
58
|
else:
|
|
58
59
|
s = np.random.uniform(self.low, self.high, (N,self.dim)).T
|
|
59
60
|
|
|
60
|
-
return s
|
|
61
|
+
return s
|
|
62
|
+
|
|
63
|
+
class UnboundedUniform(Distribution):
|
|
64
|
+
"""
|
|
65
|
+
Unbounded uniform distribution. This is a special case of the
|
|
66
|
+
Uniform distribution, where the lower and upper bounds are set to
|
|
67
|
+
-inf and inf, respectively. This distribution is not normalizable,
|
|
68
|
+
and therefore cannot be sampled from. It is mainly used for
|
|
69
|
+
initializing non-informative priors.
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
geometry : int or Geometry
|
|
73
|
+
The geometry of the distribution. If an integer is given, it is
|
|
74
|
+
interpreted as the dimension of the distribution. If a
|
|
75
|
+
Geometry object is given, its par_dim attribute is used.
|
|
76
|
+
"""
|
|
77
|
+
def __init__(self, geometry, is_symmetric=True, **kwargs):
|
|
78
|
+
super().__init__(geometry=geometry, is_symmetric=is_symmetric, **kwargs)
|
|
79
|
+
|
|
80
|
+
def logpdf(self, x):
|
|
81
|
+
"""
|
|
82
|
+
Evaluate the logarithm of the unnormalized PDF at the given values of x.
|
|
83
|
+
"""
|
|
84
|
+
# Always return 1.0 (the unnormalized log PDF)
|
|
85
|
+
return 1.0
|
|
86
|
+
|
|
87
|
+
def gradient(self, x):
|
|
88
|
+
"""
|
|
89
|
+
Computes the gradient of logpdf at the given values of x.
|
|
90
|
+
"""
|
|
91
|
+
return np.zeros_like(x)
|
|
92
|
+
|
|
93
|
+
def _sample(self, N=1, rng=None):
|
|
94
|
+
raise NotImplementedError("Cannot sample from UnboundedUniform distribution")
|
|
@@ -4,7 +4,7 @@ import math
|
|
|
4
4
|
from cuqi.experimental.mcmc import Sampler
|
|
5
5
|
from cuqi.distribution import Posterior, Gaussian, Gamma, GMRF, ModifiedHalfNormal
|
|
6
6
|
from cuqi.implicitprior import RegularizedGaussian, RegularizedGMRF, RegularizedUnboundedUniform
|
|
7
|
-
from cuqi.utilities import get_non_default_args, count_nonzero, count_constant_components_1D, count_constant_components_2D
|
|
7
|
+
from cuqi.utilities import get_non_default_args, count_nonzero, count_within_bounds, count_constant_components_1D, count_constant_components_2D, piecewise_linear_1D_DoF
|
|
8
8
|
from cuqi.geometry import Continuous1D, Continuous2D, Image2D
|
|
9
9
|
|
|
10
10
|
class Conjugate(Sampler):
|
|
@@ -17,8 +17,8 @@ class Conjugate(Sampler):
|
|
|
17
17
|
- (GMRF, Gamma) where Gamma is defined on the precision parameter of the GMRF
|
|
18
18
|
- (RegularizedGaussian, Gamma) with preset constraints only and Gamma is defined on the precision parameter of the RegularizedGaussian
|
|
19
19
|
- (RegularizedGMRF, Gamma) with preset constraints only and Gamma is defined on the precision parameter of the RegularizedGMRF
|
|
20
|
-
- (RegularizedGaussian, ModifiedHalfNormal) with preset constraints and regularization
|
|
21
|
-
- (RegularizedGMRF, ModifiedHalfNormal) with preset constraints and regularization
|
|
20
|
+
- (RegularizedGaussian, ModifiedHalfNormal) with most of the preset constraints and regularization
|
|
21
|
+
- (RegularizedGMRF, ModifiedHalfNormal) with most of the preset constraints and regularization
|
|
22
22
|
|
|
23
23
|
Currently the Gamma and ModifiedHalfNormal distribution must be univariate.
|
|
24
24
|
|
|
@@ -147,8 +147,8 @@ class _RegularizedGaussianGammaPair(_ConjugatePair):
|
|
|
147
147
|
if self.target.prior.dim != 1:
|
|
148
148
|
raise ValueError("RegularizedGaussian-Gamma conjugacy only works with univariate ModifiedHalfNormal prior")
|
|
149
149
|
|
|
150
|
-
if
|
|
151
|
-
|
|
150
|
+
# Raises error if preset is not supported
|
|
151
|
+
_compute_sparsity_level(self.target)
|
|
152
152
|
|
|
153
153
|
key_value_pairs = _get_conjugate_parameter(self.target)
|
|
154
154
|
if len(key_value_pairs) != 1:
|
|
@@ -166,7 +166,7 @@ class _RegularizedGaussianGammaPair(_ConjugatePair):
|
|
|
166
166
|
def conjugate_distribution(self):
|
|
167
167
|
# Extract variables
|
|
168
168
|
b = self.target.likelihood.data # mu
|
|
169
|
-
m =
|
|
169
|
+
m = _compute_sparsity_level(self.target)
|
|
170
170
|
Ax = self.target.likelihood.distribution.mean # x_i
|
|
171
171
|
L = self.target.likelihood.distribution(np.array([1])).sqrtprec # L
|
|
172
172
|
alpha = self.target.prior.shape # alpha
|
|
@@ -183,9 +183,9 @@ class _RegularizedUnboundedUniformGammaPair(_ConjugatePair):
|
|
|
183
183
|
if self.target.prior.dim != 1:
|
|
184
184
|
raise ValueError("RegularizedUnboundedUniform-Gamma conjugacy only works with univariate Gamma prior")
|
|
185
185
|
|
|
186
|
-
if
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
# Raises error if preset is not supported
|
|
187
|
+
_compute_sparsity_level(self.target)
|
|
188
|
+
|
|
189
189
|
key_value_pairs = _get_conjugate_parameter(self.target)
|
|
190
190
|
if len(key_value_pairs) != 1:
|
|
191
191
|
raise ValueError(f"Multiple references to conjugate parameter {self.target.prior.name} found in likelihood. Only one occurance is supported.")
|
|
@@ -219,8 +219,8 @@ class _RegularizedGaussianModifiedHalfNormalPair(_ConjugatePair):
|
|
|
219
219
|
if self.target.prior.dim != 1:
|
|
220
220
|
raise ValueError("RegularizedGaussian-ModifiedHalfNormal conjugacy only works with univariate ModifiedHalfNormal prior")
|
|
221
221
|
|
|
222
|
-
if
|
|
223
|
-
|
|
222
|
+
# Raises error if preset is not supported
|
|
223
|
+
_compute_sparsity_level(self.target)
|
|
224
224
|
|
|
225
225
|
key_value_pairs = _get_conjugate_parameter(self.target)
|
|
226
226
|
if len(key_value_pairs) != 2:
|
|
@@ -266,23 +266,74 @@ class _RegularizedGaussianModifiedHalfNormalPair(_ConjugatePair):
|
|
|
266
266
|
|
|
267
267
|
|
|
268
268
|
def _compute_sparsity_level(target):
|
|
269
|
-
"""Computes the sparsity level in accordance with Section 4 from [2],
|
|
269
|
+
"""Computes the sparsity level in accordance with Section 4 from [2],
|
|
270
|
+
this can be interpreted as the number of degrees of freedom, that is,
|
|
271
|
+
the number of components n minus the dimension the of the subdifferential of the regularized.
|
|
272
|
+
"""
|
|
270
273
|
x = target.likelihood.data
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
elif
|
|
284
|
-
|
|
285
|
-
|
|
274
|
+
|
|
275
|
+
constraint = target.likelihood.distribution.preset["constraint"]
|
|
276
|
+
regularization = target.likelihood.distribution.preset["regularization"]
|
|
277
|
+
|
|
278
|
+
# There is no reference for some of these conjugacy rules
|
|
279
|
+
if constraint == "nonnegativity":
|
|
280
|
+
if regularization in [None, "l1"]:
|
|
281
|
+
# Number of non-zero components in x
|
|
282
|
+
return count_nonzero(x)
|
|
283
|
+
elif regularization == "tv" and isinstance(target.likelihood.distribution.geometry, Continuous1D):
|
|
284
|
+
# Number of non-zero constant components in x
|
|
285
|
+
return count_constant_components_1D(x, lower = 0.0)
|
|
286
|
+
elif regularization == "tv" and isinstance(target.likelihood.distribution.geometry, (Continuous2D, Image2D)):
|
|
287
|
+
# Number of non-zero constant components in x
|
|
288
|
+
return count_constant_components_2D(target.likelihood.distribution.geometry.par2fun(x), lower = 0.0)
|
|
289
|
+
elif constraint == "box":
|
|
290
|
+
bounds = target.likelihood.distribution._box_bounds
|
|
291
|
+
if regularization is None:
|
|
292
|
+
# Number of components in x that are strictly between the lower and upper bound
|
|
293
|
+
return count_within_bounds(x, bounds[0], bounds[1])
|
|
294
|
+
elif regularization == "l1":
|
|
295
|
+
# Number of components in x that are strictly between the lower and upper bound and are not zero
|
|
296
|
+
return count_within_bounds(x, bounds[0], bounds[1], exception = 0.0)
|
|
297
|
+
elif regularization == "tv" and isinstance(target.likelihood.distribution.geometry, Continuous1D):
|
|
298
|
+
# Number of constant components in x between are strictly between the lower and upper bound
|
|
299
|
+
return count_constant_components_1D(x, lower = bounds[0], upper = bounds[1])
|
|
300
|
+
elif regularization == "tv" and isinstance(target.likelihood.distribution.geometry, (Continuous2D, Image2D)):
|
|
301
|
+
# Number of constant components in x between are strictly between the lower and upper bound
|
|
302
|
+
return count_constant_components_2D(target.likelihood.distribution.geometry.par2fun(x), lower = bounds[0], upper = bounds[1])
|
|
303
|
+
elif constraint in ["increasing", "decreasing"]:
|
|
304
|
+
if regularization is None:
|
|
305
|
+
# Number of constant components in x
|
|
306
|
+
return count_constant_components_1D(x)
|
|
307
|
+
elif regularization == "l1":
|
|
308
|
+
# Number of constant components in x that are not zero
|
|
309
|
+
return count_constant_components_1D(x, exception = 0.0)
|
|
310
|
+
elif regularization == "tv" and isinstance(target.likelihood.distribution.geometry, Continuous1D):
|
|
311
|
+
# Number of constant components in x
|
|
312
|
+
return count_constant_components_1D(x)
|
|
313
|
+
# Increasing and decreasing cannot be done in 2D
|
|
314
|
+
elif constraint in ["convex", "concave"]:
|
|
315
|
+
if regularization is None:
|
|
316
|
+
# Number of piecewise linear components in x
|
|
317
|
+
return piecewise_linear_1D_DoF(x)
|
|
318
|
+
elif regularization == "l1":
|
|
319
|
+
# Number of piecewise linear components in x that are not zero
|
|
320
|
+
return piecewise_linear_1D_DoF(x, exception_zero = True)
|
|
321
|
+
elif regularization == "tv" and isinstance(target.likelihood.distribution.geometry, Continuous1D):
|
|
322
|
+
# Number of piecewise linear components in x that are not flat
|
|
323
|
+
return piecewise_linear_1D_DoF(x, exception_flat = True)
|
|
324
|
+
# convex and concave has only been implemented in 1D
|
|
325
|
+
elif constraint == None:
|
|
326
|
+
if regularization == "l1":
|
|
327
|
+
# Number of non-zero components in x
|
|
328
|
+
return count_nonzero(x)
|
|
329
|
+
elif regularization == "tv" and isinstance(target.likelihood.distribution.geometry, Continuous1D):
|
|
330
|
+
# Number of non-zero constant components in x
|
|
331
|
+
return count_constant_components_1D(x)
|
|
332
|
+
elif regularization == "tv" and isinstance(target.likelihood.distribution.geometry, (Continuous2D, Image2D)):
|
|
333
|
+
# Number of non-zero constant components in x
|
|
334
|
+
return count_constant_components_2D(target.likelihood.distribution.geometry.par2fun(x))
|
|
335
|
+
|
|
336
|
+
raise ValueError("RegularizedGaussian preset constraint and regularization choice is currently not supported with conjugacy.")
|
|
286
337
|
|
|
287
338
|
|
|
288
339
|
def _get_conjugate_parameter(target):
|
|
@@ -2,10 +2,12 @@ from cuqi.utilities import get_non_default_args
|
|
|
2
2
|
from cuqi.distribution import Distribution, Gaussian
|
|
3
3
|
from cuqi.solver import ProjectNonnegative, ProjectBox, ProximalL1
|
|
4
4
|
from cuqi.geometry import Continuous1D, Continuous2D, Image2D
|
|
5
|
-
from cuqi.operator import FirstOrderFiniteDifference, Operator
|
|
5
|
+
from cuqi.operator import FirstOrderFiniteDifference, SecondOrderFiniteDifference, Operator
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import scipy.sparse as sparse
|
|
9
|
+
import scipy.optimize as spoptimize
|
|
10
|
+
|
|
9
11
|
from copy import copy
|
|
10
12
|
|
|
11
13
|
|
|
@@ -60,12 +62,18 @@ class RegularizedGaussian(Distribution):
|
|
|
60
62
|
min_(z in C) 0.5||x-z||_2^2.
|
|
61
63
|
|
|
62
64
|
constraint : string or None
|
|
63
|
-
Preset constraints that generate the corresponding proximal parameter.
|
|
64
|
-
|
|
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:
|
|
65
68
|
lower_bound : array_like or None
|
|
66
69
|
Lower bound of box, defaults to zero
|
|
67
70
|
upper_bound : array_like
|
|
68
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"
|
|
69
77
|
|
|
70
78
|
regularization : string or None
|
|
71
79
|
Preset regularization that generate the corresponding proximal parameter. Can be set to "l1" or 'tv'. Required for use in Gibbs in future update.
|
|
@@ -178,6 +186,28 @@ class RegularizedGaussian(Distribution):
|
|
|
178
186
|
self._proximal = lambda z, _: ProjectBox(z, _box_lower, _box_upper)
|
|
179
187
|
self._box_bounds = (np.ones(self.dim)*_box_lower, np.ones(self.dim)*_box_upper)
|
|
180
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
|
+
self._constraint_prox = lambda z, _: spoptimize.isotonic_regression(z, increasing=True).x
|
|
193
|
+
self._preset["constraint"] = "increasing"
|
|
194
|
+
elif c_lower == "decreasing":
|
|
195
|
+
if not isinstance(self.geometry, Continuous1D):
|
|
196
|
+
raise ValueError("Geometry not supported for " + c_lower)
|
|
197
|
+
self._constraint_prox = lambda z, _: spoptimize.isotonic_regression(z, increasing=False).x
|
|
198
|
+
self._preset["constraint"] = "decreasing"
|
|
199
|
+
elif c_lower == "convex":
|
|
200
|
+
if not isinstance(self.geometry, Continuous1D):
|
|
201
|
+
raise ValueError("Geometry not supported for " + c_lower)
|
|
202
|
+
self._constraint_prox = lambda z, _: -ProjectNonnegative(-z)
|
|
203
|
+
self._constraint_oper = SecondOrderFiniteDifference(self.geometry.fun_shape, bc_type='neumann')
|
|
204
|
+
self._preset["constraint"] = "convex"
|
|
205
|
+
elif c_lower == "concave":
|
|
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"] = "concave"
|
|
181
211
|
else:
|
|
182
212
|
raise ValueError("Constraint not supported.")
|
|
183
213
|
|
|
@@ -289,7 +319,7 @@ class RegularizedGaussian(Distribution):
|
|
|
289
319
|
|
|
290
320
|
@staticmethod
|
|
291
321
|
def constraint_options():
|
|
292
|
-
return ["nonnegativity", "box"]
|
|
322
|
+
return ["nonnegativity", "box", "increasing", "decreasing", "convex", "concave"]
|
|
293
323
|
|
|
294
324
|
@staticmethod
|
|
295
325
|
def regularization_options():
|
cuqi/utilities/__init__.py
CHANGED
|
@@ -14,8 +14,10 @@ from ._utilities import (
|
|
|
14
14
|
plot_1D_density,
|
|
15
15
|
plot_2D_density,
|
|
16
16
|
count_nonzero,
|
|
17
|
+
count_within_bounds,
|
|
17
18
|
count_constant_components_1D,
|
|
18
|
-
count_constant_components_2D
|
|
19
|
+
count_constant_components_2D,
|
|
20
|
+
piecewise_linear_1D_DoF
|
|
19
21
|
)
|
|
20
22
|
|
|
21
23
|
from ._get_python_variable_name import _get_python_variable_name
|
cuqi/utilities/_utilities.py
CHANGED
|
@@ -360,8 +360,29 @@ def count_nonzero(x, threshold = 1e-6):
|
|
|
360
360
|
Theshold for considering a value as nonzero.
|
|
361
361
|
"""
|
|
362
362
|
return np.sum([np.abs(v) >= threshold for v in x])
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def count_within_bounds(x, lower_bounds, upper_bounds, threshold = 1e-6, exception = np.nan):
|
|
366
|
+
""" Returns the number of values in an array whose value lies between the provided lower and upper bounds.
|
|
367
|
+
|
|
368
|
+
Parameters
|
|
369
|
+
----------
|
|
370
|
+
x : `np.ndarray`
|
|
371
|
+
Array to count elements of.
|
|
372
|
+
|
|
373
|
+
lower_bounds : `np.ndarray`
|
|
374
|
+
Lower bound on values to disregard when counting.
|
|
375
|
+
|
|
376
|
+
upper_bounds : `np.ndarray`
|
|
377
|
+
Upper bound on values to disregard.
|
|
378
|
+
|
|
379
|
+
threshold : float
|
|
380
|
+
Theshold for considering a value as nonzero.
|
|
381
|
+
"""
|
|
382
|
+
return np.sum([l + threshold <= v and v <= u - threshold and not (exception - threshold <= v and v <= exception + threshold) for v, l, u in zip(x, lower_bounds, upper_bounds)])
|
|
383
|
+
|
|
363
384
|
|
|
364
|
-
def count_constant_components_1D(x, threshold = 1e-2, lower = -np.inf, upper = np.inf):
|
|
385
|
+
def count_constant_components_1D(x, threshold = 1e-2, lower = -np.inf, upper = np.inf, exception = np.nan):
|
|
365
386
|
""" Returns the number of piecewise constant components in a one-dimensional array
|
|
366
387
|
|
|
367
388
|
Parameters
|
|
@@ -372,29 +393,40 @@ def count_constant_components_1D(x, threshold = 1e-2, lower = -np.inf, upper = n
|
|
|
372
393
|
threshold : float
|
|
373
394
|
Strict theshold on when the difference of neighbouring values is considered zero.
|
|
374
395
|
|
|
375
|
-
lower : float
|
|
396
|
+
lower : float or numpy.ndarray
|
|
376
397
|
Piecewise constant components below this value are not counted.
|
|
377
398
|
|
|
378
|
-
upper : float
|
|
399
|
+
upper : float or numpy.ndarray
|
|
379
400
|
Piecewise constant components above this value are not counted.
|
|
380
401
|
"""
|
|
381
402
|
|
|
403
|
+
if not isinstance(lower, np.ndarray):
|
|
404
|
+
lower = lower*np.ones_like(x)
|
|
405
|
+
|
|
406
|
+
if not isinstance(upper, np.ndarray):
|
|
407
|
+
upper = upper*np.ones_like(x)
|
|
408
|
+
|
|
382
409
|
counter = 0
|
|
383
|
-
|
|
410
|
+
index = 0
|
|
411
|
+
if (x[index] > lower[index] and
|
|
412
|
+
x[index] < upper[index] and
|
|
413
|
+
not (exception - threshold <= x[index] and x[index] <= exception + threshold)):
|
|
384
414
|
counter += 1
|
|
385
415
|
|
|
386
416
|
x_previous = x[0]
|
|
387
417
|
|
|
388
418
|
for x_current in x[1:]:
|
|
419
|
+
index += 1
|
|
389
420
|
if (abs(x_previous - x_current) >= threshold and
|
|
390
|
-
x_current > lower and
|
|
391
|
-
x_current < upper
|
|
421
|
+
x_current > lower[index] and
|
|
422
|
+
x_current < upper[index] and
|
|
423
|
+
not (exception - threshold <= x_current and x_current <= exception + threshold)):
|
|
392
424
|
counter += 1
|
|
393
425
|
|
|
394
426
|
x_previous = x_current
|
|
395
427
|
|
|
396
428
|
return counter
|
|
397
|
-
|
|
429
|
+
|
|
398
430
|
def count_constant_components_2D(x, threshold = 1e-2, lower = -np.inf, upper = np.inf):
|
|
399
431
|
""" Returns the number of piecewise constant components in a two-dimensional array
|
|
400
432
|
|
|
@@ -406,12 +438,19 @@ def count_constant_components_2D(x, threshold = 1e-2, lower = -np.inf, upper = n
|
|
|
406
438
|
threshold : float
|
|
407
439
|
Strict theshold on when the difference of neighbouring values is considered zero.
|
|
408
440
|
|
|
409
|
-
lower : float
|
|
441
|
+
lower : float or numpy.ndarray
|
|
410
442
|
Piecewise constant components below this value are not counted.
|
|
411
443
|
|
|
412
|
-
upper : float
|
|
444
|
+
upper : float or numpy.ndarray
|
|
413
445
|
Piecewise constant components above this value are not counted.
|
|
414
446
|
"""
|
|
447
|
+
|
|
448
|
+
if not isinstance(lower, np.ndarray):
|
|
449
|
+
lower = lower*np.ones_like(x)
|
|
450
|
+
|
|
451
|
+
if not isinstance(upper, np.ndarray):
|
|
452
|
+
upper = upper*np.ones_like(x)
|
|
453
|
+
|
|
415
454
|
filled = np.zeros_like(x, dtype = int)
|
|
416
455
|
counter = 0
|
|
417
456
|
|
|
@@ -438,8 +477,51 @@ def count_constant_components_2D(x, threshold = 1e-2, lower = -np.inf, upper = n
|
|
|
438
477
|
for i in range(x.shape[0]):
|
|
439
478
|
for j in range(x.shape[1]):
|
|
440
479
|
if filled[i,j] == 0:
|
|
441
|
-
if x[i,j] > lower and x[i,j] < upper:
|
|
480
|
+
if x[i,j] > lower[i,j] and x[i,j] < upper[i,j]:
|
|
442
481
|
counter += 1
|
|
443
482
|
process(i, j)
|
|
444
483
|
return counter
|
|
445
|
-
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def piecewise_linear_1D_DoF(x, threshold = 1e-5, exception_zero = False, exception_flat = False):
|
|
488
|
+
""" Returns the degrees of freedom of a piecewise linear signal.
|
|
489
|
+
Assuming linear interpolation, this corresponds to the number of non-differentiable points, including end-points.
|
|
490
|
+
|
|
491
|
+
Parameters
|
|
492
|
+
----------
|
|
493
|
+
x : `np.ndarray`
|
|
494
|
+
1D Array to compute degrees of freedom of.
|
|
495
|
+
|
|
496
|
+
threshold : float
|
|
497
|
+
Strict theshold on when values are considered zero.
|
|
498
|
+
|
|
499
|
+
exception_zero : Boolean
|
|
500
|
+
Whether a zero piecewise linear components should be considered.
|
|
501
|
+
|
|
502
|
+
exception_flat : Boolean
|
|
503
|
+
Whether a flat piecewise linear components should be considered.
|
|
504
|
+
"""
|
|
505
|
+
|
|
506
|
+
differences = x[1:] - x[:-1]
|
|
507
|
+
double_differences = differences[1:] - differences[:-1]
|
|
508
|
+
|
|
509
|
+
joints = [True] + [np.abs(d) >= threshold for d in double_differences] + [True]
|
|
510
|
+
|
|
511
|
+
if not exception_zero and not exception_flat:
|
|
512
|
+
return np.sum(joints)
|
|
513
|
+
elif exception_zero:
|
|
514
|
+
return np.sum(joints and [np.abs(v) < threshold for v in x])
|
|
515
|
+
elif exception_flat:
|
|
516
|
+
prev_joint = None
|
|
517
|
+
counter = 0
|
|
518
|
+
for i in range(len(joints)):
|
|
519
|
+
if joints[i]:
|
|
520
|
+
counter += 1
|
|
521
|
+
if prev_joint is None:
|
|
522
|
+
prev_joint = i
|
|
523
|
+
else:
|
|
524
|
+
if np.abs(x[i] - x[prev_joint]) < threshold:
|
|
525
|
+
counter -= 1
|
|
526
|
+
prev_joint = i
|
|
527
|
+
return counter
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: CUQIpy
|
|
3
|
-
Version: 1.3.0.post0.
|
|
3
|
+
Version: 1.3.0.post0.dev70
|
|
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
|
|
@@ -205,6 +205,7 @@ Requires-Dist: numpy>=1.17.0
|
|
|
205
205
|
Requires-Dist: scipy<1.13
|
|
206
206
|
Requires-Dist: arviz
|
|
207
207
|
Requires-Dist: tqdm
|
|
208
|
+
Dynamic: license-file
|
|
208
209
|
|
|
209
210
|
<div align="center">
|
|
210
211
|
<img src="https://cuqi-dtu.github.io/CUQIpy/_static/logo.png" alt="CUQIpy logo" width="250"/>
|
|
@@ -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=x3zpGp2Yt3uRSPN-QiV1WPjz_E6y4Vd8e_5YzWKqC-4,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
|
|
@@ -14,7 +14,7 @@ cuqi/data/cookie.png,sha256=mr6wUeoIUc5VC2qYj8vafOmTbcRwz0fHz4IIPK9_PnE,984680
|
|
|
14
14
|
cuqi/data/satellite.mat,sha256=a0Nz_Ak-Y0m360dH74pa_rpk-MhaQ91ftGTKhQX7I8g,16373
|
|
15
15
|
cuqi/density/__init__.py,sha256=0zfVcPgqdqiPkss5n_WP_PUt-G3ovHXjokhqEKIlLwA,48
|
|
16
16
|
cuqi/density/_density.py,sha256=BG7gtP0cbFYLVgjYQGkNAhM95PR5ocBVLKRlOVX2PyM,7253
|
|
17
|
-
cuqi/distribution/__init__.py,sha256=
|
|
17
|
+
cuqi/distribution/__init__.py,sha256=f-HM-SUrvPO66_FAJ6k4TffBq4H94OusRMDOJgcJU2w,779
|
|
18
18
|
cuqi/distribution/_beta.py,sha256=QlibnuHNcvWjl-du5aRc9QuzS3n4PsyD_8Nc47w-E0Q,2903
|
|
19
19
|
cuqi/distribution/_cauchy.py,sha256=Qwi21WkwUBnBkLbhR-yCGO0tQ_U_3mmvR0pDMPPPB5c,3296
|
|
20
20
|
cuqi/distribution/_cmrf.py,sha256=tCbEulM_O7FB3C_W-3IqZp9zGHkTofCdFF0ybHc9UZI,3745
|
|
@@ -33,7 +33,7 @@ cuqi/distribution/_normal.py,sha256=vhIiAseW09IKh1uy0KUq7RP1IuY7hH5aNM1W_R8Gd_Q,
|
|
|
33
33
|
cuqi/distribution/_posterior.py,sha256=zAfL0GECxekZ2lBt1W6_LN0U_xskMwK4VNce5xAF7ig,5018
|
|
34
34
|
cuqi/distribution/_smoothed_laplace.py,sha256=p-1Y23mYA9omwiHGkEuv3T2mwcPAAoNlCr7T8osNkjE,2925
|
|
35
35
|
cuqi/distribution/_truncated_normal.py,sha256=sPaveXTij_yRZg-609zfwzGjHJOzZVenvYaYK0krUec,4240
|
|
36
|
-
cuqi/distribution/_uniform.py,sha256=
|
|
36
|
+
cuqi/distribution/_uniform.py,sha256=eONkPlVX3EIZ9x1vHa3Ul60NJsbuWVCabTuAJVH2GGk,3255
|
|
37
37
|
cuqi/experimental/__init__.py,sha256=bIQ9OroeitHbwgNe3wI_JvzkILK0N25Tt7wpquPoU3w,129
|
|
38
38
|
cuqi/experimental/algebra/__init__.py,sha256=btRAWG58ZfdtK0afXKOg60AX7d76KMBjlZa4AWBCCgU,81
|
|
39
39
|
cuqi/experimental/algebra/_ast.py,sha256=PdPz19cJMjvnMx4KEzhn4gvxIZX_UViE33Mbttj_5Xw,9873
|
|
@@ -42,7 +42,7 @@ cuqi/experimental/algebra/_randomvariable.py,sha256=isbFtIWsWXF-yF5Vb56nLy4MCkQM
|
|
|
42
42
|
cuqi/experimental/geometry/__init__.py,sha256=kgoKegfz3Jhr7fpORB_l55z9zLZRtloTLyXFDh1oF2o,47
|
|
43
43
|
cuqi/experimental/geometry/_productgeometry.py,sha256=G-hIYnfLiRS5IWD2EPXORNBKNP2zSaCCHAeBlDC_R3I,7177
|
|
44
44
|
cuqi/experimental/mcmc/__init__.py,sha256=zSqLZmxOqQ-F94C9-gPv7g89TX1XxlrlNm071Eb167I,4487
|
|
45
|
-
cuqi/experimental/mcmc/_conjugate.py,sha256=
|
|
45
|
+
cuqi/experimental/mcmc/_conjugate.py,sha256=vqucxC--pihBCUcupdcIo4ymDPPjmMKGb7OL1THjaKE,22059
|
|
46
46
|
cuqi/experimental/mcmc/_conjugate_approx.py,sha256=jmxe2FEbO9fwpc8opyjJ2px0oed3dGyj0qDwyHo4aOk,3545
|
|
47
47
|
cuqi/experimental/mcmc/_cwmh.py,sha256=cAvtc3tex53ZUKPMGwj2RIkHAZurpqphko8nk8_DmJs,7340
|
|
48
48
|
cuqi/experimental/mcmc/_direct.py,sha256=9pQS_2Qk2-ybt6m8WTfPoKetcxQ00WaTRN85-Z0FrBY,777
|
|
@@ -59,7 +59,7 @@ cuqi/geometry/__init__.py,sha256=Tz1WGzZBY-QGH3c0GiyKm9XHN8MGGcnU6TUHLZkzB3o,842
|
|
|
59
59
|
cuqi/geometry/_geometry.py,sha256=W-oQTZPelVS7fN9qZj6bNBuh-yY0eqOHJ39UwB-WmQY,47562
|
|
60
60
|
cuqi/implicitprior/__init__.py,sha256=6z3lvw-tWDyjZSpB3pYzvijSMK9Zlf1IYqOVTtMD2h4,309
|
|
61
61
|
cuqi/implicitprior/_regularizedGMRF.py,sha256=BUeT4rwJzary9K56fkxCNGCeKZd-2VSgOT8XNHxFPRE,6345
|
|
62
|
-
cuqi/implicitprior/_regularizedGaussian.py,sha256=
|
|
62
|
+
cuqi/implicitprior/_regularizedGaussian.py,sha256=gFGtIJH6Zh7vwIrQ2yON4x5VtzJngTa4mCyGOYEm9ac,21252
|
|
63
63
|
cuqi/implicitprior/_regularizedUnboundedUniform.py,sha256=uHGYYnTjVxdPbY-5JwocFOH0sHRfGrrLiHWahzH9R8A,3533
|
|
64
64
|
cuqi/implicitprior/_restorator.py,sha256=Z350XUJEt7N59Qw-SIUaBljQNDJk4Zb0i_KRFrt2DCg,10087
|
|
65
65
|
cuqi/likelihood/__init__.py,sha256=QXif382iwZ5bT3ZUqmMs_n70JVbbjxbqMrlQYbMn4Zo,1776
|
|
@@ -90,11 +90,11 @@ cuqi/solver/__init__.py,sha256=KYgAi_8VoAwljTB3S2I87YnJkRtedskLee7hQp_-zp8,220
|
|
|
90
90
|
cuqi/solver/_solver.py,sha256=_Q47Atv8Ze6eMJzA22s0OzdW4lcDigRhbotnCzmrQWE,30662
|
|
91
91
|
cuqi/testproblem/__init__.py,sha256=DWTOcyuNHMbhEuuWlY5CkYkNDSAqhvsKmJXBLivyblU,202
|
|
92
92
|
cuqi/testproblem/_testproblem.py,sha256=x769LwwRdJdzIiZkcQUGb_5-vynNTNALXWKato7sS0Q,52540
|
|
93
|
-
cuqi/utilities/__init__.py,sha256=
|
|
93
|
+
cuqi/utilities/__init__.py,sha256=d5QXRzmI6EchS9T4b7eTezSisPWuWklO8ey4YBx9kI0,569
|
|
94
94
|
cuqi/utilities/_get_python_variable_name.py,sha256=wxpCaj9f3ZtBNqlGmmuGiITgBaTsY-r94lUIlK6UAU4,2043
|
|
95
|
-
cuqi/utilities/_utilities.py,sha256=
|
|
96
|
-
cuqipy-1.3.0.post0.
|
|
97
|
-
cuqipy-1.3.0.post0.
|
|
98
|
-
cuqipy-1.3.0.post0.
|
|
99
|
-
cuqipy-1.3.0.post0.
|
|
100
|
-
cuqipy-1.3.0.post0.
|
|
95
|
+
cuqi/utilities/_utilities.py,sha256=en4woCU0LJvdrnmjns5OMLMmRguWWTQxhBMuFGxPK4U,18415
|
|
96
|
+
cuqipy-1.3.0.post0.dev70.dist-info/licenses/LICENSE,sha256=kJWRPrtRoQoZGXyyvu50Uc91X6_0XRaVfT0YZssicys,10799
|
|
97
|
+
cuqipy-1.3.0.post0.dev70.dist-info/METADATA,sha256=p9-ZWhhvEJ2jhjPRIldlRrO6bF2hGYKN9AeEuBYs0WM,18550
|
|
98
|
+
cuqipy-1.3.0.post0.dev70.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
99
|
+
cuqipy-1.3.0.post0.dev70.dist-info/top_level.txt,sha256=AgmgMc6TKfPPqbjV0kvAoCBN334i_Lwwojc7HE3ZwD0,5
|
|
100
|
+
cuqipy-1.3.0.post0.dev70.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|