CUQIpy 0.8.0__tar.gz → 0.8.0.post0.dev13__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.
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/CUQIpy.egg-info/PKG-INFO +1 -1
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/PKG-INFO +1 -1
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/_version.py +3 -3
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_cmrf.py +1 -1
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_gmrf.py +49 -56
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_lmrf.py +1 -1
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/samples/_samples.py +15 -1
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_MRFs.py +27 -1
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_bayesian_inversion.py +1 -1
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_distribution.py +4 -4
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_distributions_shape.py +0 -3
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_sampler.py +3 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/CUQIpy.egg-info/SOURCES.txt +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/CUQIpy.egg-info/dependency_links.txt +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/CUQIpy.egg-info/requires.txt +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/CUQIpy.egg-info/top_level.txt +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/LICENSE +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/README.md +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/_messages.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/array/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/array/_array.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/config.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/data/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/data/_data.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/data/astronaut.npz +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/data/camera.npz +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/data/cat.npz +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/data/satellite.mat +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/density/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/density/_density.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/diagnostics.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_beta.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_cauchy.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_custom.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_distribution.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_gamma.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_gaussian.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_inverse_gamma.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_joint_distribution.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_laplace.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_lognormal.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_normal.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_posterior.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/distribution/_uniform.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/geometry/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/geometry/_geometry.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/likelihood/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/likelihood/_likelihood.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/model/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/model/_model.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/operator/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/operator/_operator.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/pde/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/pde/_pde.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/problem/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/problem/_problem.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_conjugate.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_conjugate_approx.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_cwmh.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_gibbs.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_hmc.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_langevin_algorithm.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_laplace_approximation.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_mh.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_pcn.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_rto.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/sampler/_sampler.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/samples/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/solver/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/solver/_solver.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/testproblem/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/testproblem/_testproblem.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/utilities/__init__.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/utilities/_get_python_variable_name.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/cuqi/utilities/_utilities.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/pyproject.toml +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/requirements.txt +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/setup.cfg +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/setup.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_abstract_distribution_density.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_density.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_geometry.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_joint_distribution.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_likelihood.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_model.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_pde.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_posterior.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_problem.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_samples.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_solver.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_testproblem.py +0 -0
- {CUQIpy-0.8.0 → CUQIpy-0.8.0.post0.dev13}/tests/test_utilities.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: CUQIpy
|
|
3
|
-
Version: 0.8.0
|
|
3
|
+
Version: 0.8.0.post0.dev13
|
|
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: 0.8.0
|
|
3
|
+
Version: 0.8.0.post0.dev13
|
|
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": "2023-
|
|
11
|
+
"date": "2023-12-19T22:07:12+0100",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "0.8.0"
|
|
14
|
+
"full-revisionid": "cdddb1ac7225b019877b440f2c84a5cf34190af5",
|
|
15
|
+
"version": "0.8.0.post0.dev13"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -2,11 +2,12 @@ import numpy as np
|
|
|
2
2
|
from scipy.sparse import diags, eye
|
|
3
3
|
from scipy.sparse import linalg as splinalg
|
|
4
4
|
from scipy.linalg import dft
|
|
5
|
-
from cuqi.geometry import
|
|
5
|
+
from cuqi.geometry import _get_identity_geometries
|
|
6
6
|
from cuqi.utilities import sparse_cholesky
|
|
7
7
|
from cuqi import config
|
|
8
8
|
from cuqi.operator import PrecisionFiniteDifference
|
|
9
9
|
from cuqi.distribution import Distribution
|
|
10
|
+
from cuqi.utilities import force_ndarray
|
|
10
11
|
|
|
11
12
|
class GMRF(Distribution):
|
|
12
13
|
""" Gaussian Markov random field (GMRF).
|
|
@@ -19,9 +20,6 @@ class GMRF(Distribution):
|
|
|
19
20
|
prec : float
|
|
20
21
|
Precision of the GMRF.
|
|
21
22
|
|
|
22
|
-
physical_dim : int
|
|
23
|
-
The physical dimension of what the distribution represents (can take the values 1 or 2).
|
|
24
|
-
|
|
25
23
|
bc_type : str
|
|
26
24
|
The type of boundary conditions to use. Can be 'zero', 'periodic' or 'neumann'.
|
|
27
25
|
|
|
@@ -94,54 +92,67 @@ class GMRF(Distribution):
|
|
|
94
92
|
For more details see: See Bardsley, J. (2018). Computational Uncertainty Quantification for Inverse Problems, Chapter 4.2.
|
|
95
93
|
|
|
96
94
|
"""
|
|
97
|
-
|
|
98
|
-
def __init__(self, mean, prec, physical_dim=1, bc_type='zero', order=1, is_symmetric=True, **kwargs):
|
|
99
|
-
super().__init__(is_symmetric=is_symmetric, **kwargs) #TODO: This calls Distribution __init__, should be replaced by calling Gaussian.__init__
|
|
100
95
|
|
|
101
|
-
|
|
96
|
+
def __init__(self, mean=None, prec=None, bc_type="zero", order=1, **kwargs):
|
|
97
|
+
# Init from abstract distribution class
|
|
98
|
+
super().__init__(**kwargs)
|
|
99
|
+
|
|
100
|
+
self.mean = mean
|
|
102
101
|
self.prec = prec
|
|
103
|
-
self.
|
|
104
|
-
|
|
102
|
+
self._bc_type = bc_type
|
|
103
|
+
|
|
104
|
+
# Ensure geometry has shape
|
|
105
|
+
if not self.geometry.fun_shape or self.geometry.par_dim == 1:
|
|
106
|
+
raise ValueError(f"Distribution {self.__class__.__name__} must be initialized with supported geometry (geometry of which the fun_shape is not None) and has parameter dimension greater than 1.")
|
|
107
|
+
|
|
108
|
+
# Default physical_dim to geometry's dimension if not provided
|
|
109
|
+
physical_dim = len(self.geometry.fun_shape)
|
|
110
|
+
|
|
111
|
+
# Ensure provided physical dimension is either 1 or 2
|
|
112
|
+
if physical_dim not in [1, 2]:
|
|
113
|
+
raise ValueError("Only physical dimension 1 or 2 supported.")
|
|
114
|
+
|
|
105
115
|
self._physical_dim = physical_dim
|
|
106
|
-
|
|
107
|
-
num_nodes = tuple(self._partition_size for _ in range(physical_dim))
|
|
108
|
-
if physical_dim == 2: #TODO. Remove once _DefaultGeometry is implemented for 2D.
|
|
109
|
-
if isinstance(self.geometry, _DefaultGeometry1D):
|
|
110
|
-
self.geometry = Image2D(num_nodes)
|
|
111
116
|
|
|
112
|
-
self.
|
|
113
|
-
|
|
117
|
+
if self._physical_dim == 2:
|
|
118
|
+
N = int(np.sqrt(self.dim))
|
|
119
|
+
num_nodes = (N, N)
|
|
120
|
+
else:
|
|
121
|
+
num_nodes = self.dim
|
|
122
|
+
|
|
123
|
+
self._prec_op = PrecisionFiniteDifference(num_nodes=num_nodes, bc_type=bc_type, order=order)
|
|
124
|
+
self._diff_op = self._prec_op._diff_op
|
|
114
125
|
|
|
115
126
|
# compute Cholesky and det
|
|
116
127
|
if (bc_type == 'zero'): # only for PSD matrices
|
|
117
128
|
self._rank = self.dim
|
|
118
129
|
self._chol = sparse_cholesky(self._prec_op.get_matrix()).T
|
|
119
130
|
self._logdet = 2*sum(np.log(self._chol.diagonal()))
|
|
120
|
-
# L_cholmod = cholesky(self.L, ordering_method='natural')
|
|
121
|
-
# self.chol = L_cholmod
|
|
122
|
-
# self.logdet = L_cholmod.logdet()
|
|
123
|
-
#
|
|
124
|
-
# np.log(np.linalg.det(self.L.todense()))
|
|
125
131
|
elif (bc_type == 'periodic') or (bc_type == 'neumann'):
|
|
126
|
-
# Print warning that periodic and Neumann boundary conditions are experimental
|
|
127
132
|
print("Warning (GMRF): Periodic and Neumann boundary conditions are experimental. Sampling using LinearRTO may not produce fully accurate results.")
|
|
128
|
-
|
|
129
133
|
eps = np.finfo(float).eps
|
|
130
134
|
self._rank = self.dim - 1 #np.linalg.matrix_rank(self.L.todense())
|
|
131
135
|
self._chol = sparse_cholesky(self._prec_op + np.sqrt(eps)*eye(self.dim, dtype=int)).T
|
|
132
|
-
if (self.dim > config.MAX_DIM_INV): # approximate to avoid '
|
|
136
|
+
if (self.dim > config.MAX_DIM_INV): # approximate to avoid 'excessive' time
|
|
133
137
|
self._logdet = 2*sum(np.log(self._chol.diagonal()))
|
|
134
138
|
else:
|
|
135
|
-
# eigval = eigvalsh(self.L.todense())
|
|
136
139
|
self._L_eigval = splinalg.eigsh(self._prec_op.get_matrix(), self._rank, which='LM', return_eigenvectors=False)
|
|
137
140
|
self._logdet = sum(np.log(self._L_eigval))
|
|
138
141
|
else:
|
|
139
142
|
raise ValueError('bc_type must be "zero", "periodic" or "neumann"')
|
|
140
143
|
|
|
144
|
+
@property
|
|
145
|
+
def mean(self):
|
|
146
|
+
return self._mean
|
|
147
|
+
|
|
148
|
+
@mean.setter
|
|
149
|
+
def mean(self, value):
|
|
150
|
+
self._mean = force_ndarray(value, flatten=True)
|
|
151
|
+
|
|
141
152
|
@property
|
|
142
153
|
def prec(self):
|
|
143
154
|
return self._prec
|
|
144
|
-
|
|
155
|
+
|
|
145
156
|
@prec.setter
|
|
146
157
|
def prec(self, value):
|
|
147
158
|
# We store the precision as a scalar to match existing code in this class,
|
|
@@ -153,33 +164,20 @@ class GMRF(Distribution):
|
|
|
153
164
|
raise ValueError('Precision must be a scalar or a 1D array with a single scalar element.')
|
|
154
165
|
self._prec = value
|
|
155
166
|
|
|
156
|
-
@property
|
|
157
|
-
def dim(self):
|
|
158
|
-
if self._physical_dim == 1:
|
|
159
|
-
return self._partition_size
|
|
160
|
-
elif self._physical_dim==2:
|
|
161
|
-
return self._partition_size**2
|
|
162
|
-
raise ValueError("attribute dom can be either 1 or 2")
|
|
163
|
-
|
|
164
167
|
def logpdf(self, x):
|
|
165
|
-
mean = self.mean
|
|
168
|
+
mean = self.mean
|
|
166
169
|
const = 0.5*(self._rank*(np.log(self.prec)-np.log(2*np.pi)) + self._logdet)
|
|
167
|
-
|
|
168
|
-
# = sps.multivariate_normal.logpdf(x.T, self.mean.flatten(), np.linalg.inv(self.prec*self.L.todense()))
|
|
169
|
-
return y
|
|
170
|
-
|
|
171
|
-
def pdf(self, x):
|
|
172
|
-
# = sps.multivariate_normal.pdf(x.T, self.mean.flatten(), np.linalg.inv(self.prec*self.L.todense()))
|
|
173
|
-
return np.exp(self.logpdf(x))
|
|
170
|
+
return const - 0.5*( self.prec*((x-mean).T @ (self._prec_op @ (x-mean))) )
|
|
174
171
|
|
|
175
172
|
def _gradient(self, x):
|
|
176
173
|
#Avoid complicated geometries that change the gradient.
|
|
177
174
|
if not type(self.geometry) in _get_identity_geometries():
|
|
178
175
|
raise NotImplementedError("Gradient not implemented for distribution {} with geometry {}".format(self,self.geometry))
|
|
179
176
|
|
|
180
|
-
if not callable(self.mean):
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
if not callable(self.mean): # for prior
|
|
178
|
+
return -(self.prec*self._prec_op) @ (x-self.mean)
|
|
179
|
+
else:
|
|
180
|
+
NotImplementedError("Gradient not implemented for mean {}".format(type(self.mean)))
|
|
183
181
|
|
|
184
182
|
def _sample(self, N=1, rng=None):
|
|
185
183
|
if (self._bc_type == 'zero'):
|
|
@@ -190,10 +188,9 @@ class GMRF(Distribution):
|
|
|
190
188
|
xi = np.random.randn(self.dim, N) # standard Gaussian
|
|
191
189
|
|
|
192
190
|
if N == 1:
|
|
193
|
-
s = self.mean.flatten() + (1/np.sqrt(self.prec))*splinalg.spsolve(self._chol.T, xi)
|
|
194
|
-
else:
|
|
195
191
|
s = self.mean + (1/np.sqrt(self.prec))*splinalg.spsolve(self._chol.T, xi)
|
|
196
|
-
|
|
192
|
+
else:
|
|
193
|
+
s = self.mean[:, np.newaxis] + (1/np.sqrt(self.prec))*splinalg.spsolve(self._chol.T, xi)
|
|
197
194
|
|
|
198
195
|
elif (self._bc_type == 'periodic'):
|
|
199
196
|
|
|
@@ -206,12 +203,9 @@ class GMRF(Distribution):
|
|
|
206
203
|
xi = np.random.randn(self.dim, N) + 1j*np.random.randn(self.dim, N)
|
|
207
204
|
|
|
208
205
|
F = dft(self.dim, scale='sqrtn') # unitary DFT matrix
|
|
209
|
-
# eigv = eigvalsh(self.L.todense()) # splinalg.eigsh(self.L, self.rank, return_eigenvectors=False)
|
|
210
206
|
eigv = np.hstack([self._L_eigval, self._L_eigval[-1]]) # repeat last eigval to complete dim
|
|
211
207
|
L_sqrt = diags(np.sqrt(eigv))
|
|
212
|
-
s = self.mean + (1/np.sqrt(self.prec))*np.real(F.conj() @ splinalg.spsolve(L_sqrt, xi))
|
|
213
|
-
# L_sqrt = pinvh(np.diag(np.sqrt(eigv)))
|
|
214
|
-
# s = self.mean + (1/np.sqrt(self.prec))*np.real(F.conj() @ (L_sqrt @ xi))
|
|
208
|
+
s = self.mean[:, np.newaxis] + (1/np.sqrt(self.prec))*np.real(F.conj() @ splinalg.spsolve(L_sqrt, xi))
|
|
215
209
|
|
|
216
210
|
elif (self._bc_type == 'neumann'):
|
|
217
211
|
|
|
@@ -220,7 +214,7 @@ class GMRF(Distribution):
|
|
|
220
214
|
else:
|
|
221
215
|
xi = np.random.randn(self._diff_op.shape[0], N) # standard Gaussian
|
|
222
216
|
|
|
223
|
-
s = self.mean + (1/np.sqrt(self.prec))* \
|
|
217
|
+
s = self.mean[:, np.newaxis] + (1/np.sqrt(self.prec))* \
|
|
224
218
|
splinalg.spsolve(self._chol.T, (splinalg.spsolve(self._chol, (self._diff_op.T @ xi))))
|
|
225
219
|
else:
|
|
226
220
|
raise TypeError('Unexpected BC type (choose from zero, periodic, neumann or none)')
|
|
@@ -233,5 +227,4 @@ class GMRF(Distribution):
|
|
|
233
227
|
|
|
234
228
|
@property
|
|
235
229
|
def sqrtprecTimesMean(self):
|
|
236
|
-
return (self.sqrtprec@self.mean)
|
|
237
|
-
|
|
230
|
+
return (self.sqrtprec@self.mean)
|
|
@@ -40,7 +40,7 @@ class LMRF(Distribution):
|
|
|
40
40
|
prior = cuqi.distribution.LMRF(location=0, scale=0.1, geometry=128)
|
|
41
41
|
|
|
42
42
|
"""
|
|
43
|
-
def __init__(self, location, scale, bc_type="zero", **kwargs):
|
|
43
|
+
def __init__(self, location=None, scale=None, bc_type="zero", **kwargs):
|
|
44
44
|
# Init from abstract distribution class
|
|
45
45
|
super().__init__(**kwargs)
|
|
46
46
|
|
|
@@ -5,9 +5,17 @@ from cuqi.geometry import _DefaultGeometry1D, Continuous2D, Image2D
|
|
|
5
5
|
from cuqi.array import CUQIarray
|
|
6
6
|
from cuqi.utilities import force_ndarray
|
|
7
7
|
from copy import copy
|
|
8
|
-
import arviz # Plotting tool
|
|
9
8
|
from numbers import Number
|
|
10
9
|
|
|
10
|
+
try:
|
|
11
|
+
import arviz # Plotting tool
|
|
12
|
+
except ImportError:
|
|
13
|
+
arviz = None
|
|
14
|
+
|
|
15
|
+
def _check_for_arviz():
|
|
16
|
+
if arviz is None:
|
|
17
|
+
raise ImportError("The arviz package is required for this functionality. Please install arviz using `pip install arviz`.")
|
|
18
|
+
|
|
11
19
|
|
|
12
20
|
class Samples(object):
|
|
13
21
|
"""
|
|
@@ -598,6 +606,7 @@ class Samples(object):
|
|
|
598
606
|
datadict = self.to_arviz_inferencedata(variable_indices)
|
|
599
607
|
|
|
600
608
|
# Plot autocorrelation using arviz
|
|
609
|
+
_check_for_arviz()
|
|
601
610
|
axis = arviz.plot_autocorr(datadict, max_lag=max_lag, combined=combined, **kwargs)
|
|
602
611
|
|
|
603
612
|
return axis
|
|
@@ -664,6 +673,7 @@ class Samples(object):
|
|
|
664
673
|
kwargs["lines"] = tuple([(par_names[i], {}, exact[i]) for i in range(len(par_names))])
|
|
665
674
|
|
|
666
675
|
# Plot using arviz
|
|
676
|
+
_check_for_arviz()
|
|
667
677
|
ax = arviz.plot_trace(datadict, combined=combined, **kwargs)
|
|
668
678
|
|
|
669
679
|
# Improves subplot spacing
|
|
@@ -704,6 +714,7 @@ class Samples(object):
|
|
|
704
714
|
# Convert to arviz InferenceData object
|
|
705
715
|
datadict = self.to_arviz_inferencedata(variable_indices)
|
|
706
716
|
|
|
717
|
+
_check_for_arviz()
|
|
707
718
|
ax = arviz.plot_pair(datadict, kind=kind, marginals=marginals, **kwargs)
|
|
708
719
|
|
|
709
720
|
return ax
|
|
@@ -751,6 +762,7 @@ class Samples(object):
|
|
|
751
762
|
-------
|
|
752
763
|
Numpy array with effective sample size for each variable.
|
|
753
764
|
"""
|
|
765
|
+
_check_for_arviz()
|
|
754
766
|
ESS_xarray = arviz.ess(self.to_arviz_inferencedata(), **kwargs)
|
|
755
767
|
ESS_items = ESS_xarray.items()
|
|
756
768
|
ESS = np.empty(len(ESS_items))
|
|
@@ -806,6 +818,7 @@ class Samples(object):
|
|
|
806
818
|
datadict = dict(zip(variables,samples))
|
|
807
819
|
|
|
808
820
|
# Compute rhat
|
|
821
|
+
_check_for_arviz()
|
|
809
822
|
RHAT_xarray = arviz.rhat(datadict, **kwargs)
|
|
810
823
|
|
|
811
824
|
# Convert to numpy array
|
|
@@ -844,6 +857,7 @@ class Samples(object):
|
|
|
844
857
|
datadict = self.to_arviz_inferencedata(variable_indices)
|
|
845
858
|
|
|
846
859
|
# Plot using arviz
|
|
860
|
+
_check_for_arviz()
|
|
847
861
|
ax = arviz.plot_violin(datadict, **kwargs)
|
|
848
862
|
|
|
849
863
|
return ax
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import pytest
|
|
2
|
-
from cuqi.distribution import LMRF, CMRF
|
|
2
|
+
from cuqi.distribution import LMRF, CMRF, GMRF
|
|
3
3
|
from cuqi.geometry import Image2D, Geometry, _DefaultGeometry2D
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
@@ -68,4 +68,30 @@ def test_CMRF_dim_vs_ndarray_location():
|
|
|
68
68
|
assert cmrf1.logd(np.zeros(16)) == cmrf2.logd(np.zeros(16))
|
|
69
69
|
assert np.allclose(cmrf1.gradient(np.zeros(16)),cmrf2.gradient(np.zeros(16)))
|
|
70
70
|
|
|
71
|
+
# GMRF tests
|
|
72
|
+
def test_GMRF_should_not_allow_None_or_scalar_geometry():
|
|
73
|
+
with pytest.raises(ValueError, match="supported geometry"):
|
|
74
|
+
GMRF(0, 0.2)
|
|
75
|
+
|
|
76
|
+
@pytest.mark.parametrize("physical_dim", [-1, 0, 3])
|
|
77
|
+
def test_GMRF_invalid_physical_dim_raises_error(physical_dim):
|
|
78
|
+
with pytest.raises(ValueError, match="Only physical dimension 1 or 2 supported."):
|
|
79
|
+
GMRF(0, 0.2, geometry=Image3D())
|
|
80
|
+
|
|
81
|
+
def test_GMRF_mismatched_location_and_geometry_raises_error():
|
|
82
|
+
image_2d_geometry = Image2D((8, 8))
|
|
83
|
+
with pytest.raises(TypeError):
|
|
84
|
+
GMRF(np.zeros(3), 0.2, geometry=image_2d_geometry)
|
|
85
|
+
|
|
86
|
+
def test_GMRF_default_geometry_is_replaced_when_physical_dim_is_2():
|
|
87
|
+
gmrf = GMRF(0, 0.2, geometry=(4,4))
|
|
88
|
+
assert isinstance(gmrf.geometry, _DefaultGeometry2D)
|
|
89
|
+
|
|
90
|
+
def test_GMRF_dim_vs_ndarray_location():
|
|
91
|
+
gmrf1 = GMRF(0, 0.2, geometry=(4,4))
|
|
92
|
+
gmrf2 = GMRF(np.zeros(16), 0.2, geometry=Image2D((4,4)))
|
|
93
|
+
assert gmrf1.dim == gmrf2.dim
|
|
94
|
+
assert gmrf1.logd(np.zeros(16)) == gmrf2.logd(np.zeros(16))
|
|
95
|
+
assert np.allclose(gmrf1.gradient(np.zeros(16)),gmrf2.gradient(np.zeros(16)))
|
|
96
|
+
|
|
71
97
|
|
|
@@ -13,7 +13,7 @@ from cuqi.density import Density
|
|
|
13
13
|
@pytest.mark.parametrize("TP_type, phantom, prior, Ns",
|
|
14
14
|
[
|
|
15
15
|
(Deconvolution1D, "gauss", Gaussian(np.zeros(128), 0.071**2), 20),
|
|
16
|
-
(Deconvolution1D, "gauss", GMRF(np.zeros(128), 100,
|
|
16
|
+
(Deconvolution1D, "gauss", GMRF(np.zeros(128), 100, "zero"), 20),
|
|
17
17
|
(Deconvolution1D, "square", LMRF(0, 0.005, geometry=128), 100),
|
|
18
18
|
(Deconvolution1D, "square", CMRF(np.zeros(128), 0.01), 50),
|
|
19
19
|
])
|
|
@@ -113,7 +113,7 @@ def test_Gaussian_rng(mean,std,R):
|
|
|
113
113
|
cov = np.diag(std) @ (R @ np.diag(std))
|
|
114
114
|
assert np.allclose(cuqi.distribution.Gaussian(mean,cov).sample(10).samples,cuqi.distribution.Gaussian(mean,cov).sample(10,rng=rng).samples)
|
|
115
115
|
|
|
116
|
-
@pytest.mark.parametrize("dist",[cuqi.distribution.GMRF(np.ones(128),35,
|
|
116
|
+
@pytest.mark.parametrize("dist",[cuqi.distribution.GMRF(np.ones(128),35,'zero'),cuqi.distribution.GMRF(np.ones(128),35,'periodic'),cuqi.distribution.GMRF(np.ones(128),35,'neumann')])
|
|
117
117
|
def test_GMRF_rng(dist):
|
|
118
118
|
np.random.seed(3)
|
|
119
119
|
rng = np.random.RandomState(3)
|
|
@@ -237,7 +237,7 @@ def test_Gaussians_vs_GMRF(prec, GMRF_order):
|
|
|
237
237
|
X_cov = cuqi.distribution.Gaussian(np.zeros(dim), cov)
|
|
238
238
|
X_sqrtprec = cuqi.distribution.Gaussian(np.zeros(dim), sqrtprec=sqrtprec)
|
|
239
239
|
X_sqrtcov = cuqi.distribution.Gaussian(np.zeros(dim), sqrtcov=sqrtcov)
|
|
240
|
-
X_GMRF = cuqi.distribution.GMRF(np.zeros(dim), 1,
|
|
240
|
+
X_GMRF = cuqi.distribution.GMRF(np.zeros(dim), 1, 'zero', order=GMRF_order)
|
|
241
241
|
|
|
242
242
|
# logpdfs for full matrix
|
|
243
243
|
x0 = np.random.randn(dim)
|
|
@@ -575,7 +575,7 @@ def test_Gaussian_Cov_sample(C):
|
|
|
575
575
|
cuqi.distribution.Gaussian(np.zeros(2),
|
|
576
576
|
np.array([[1.0, 0.7],
|
|
577
577
|
[0.7, 1.]])),
|
|
578
|
-
cuqi.distribution.GMRF(np.zeros(2), 0.1
|
|
578
|
+
cuqi.distribution.GMRF(np.zeros(2), 0.1)])
|
|
579
579
|
def test_enable_FD_gradient_distributions(dist):
|
|
580
580
|
"""Test that the distribution FD gradient is close to the exact gradient"""
|
|
581
581
|
x = np.array([0.1, 0.3])
|
|
@@ -596,7 +596,7 @@ def test_enable_FD_gradient_distributions(dist):
|
|
|
596
596
|
[cuqi.distribution.Gaussian(np.zeros(6), np.eye(6)),
|
|
597
597
|
cuqi.distribution.Beta(np.ones(6)*2, 5),
|
|
598
598
|
cuqi.distribution.Lognormal(np.ones(6)*.1, 4),
|
|
599
|
-
cuqi.distribution.GMRF(np.zeros(6), 0.1
|
|
599
|
+
cuqi.distribution.GMRF(np.zeros(6), 0.1)])
|
|
600
600
|
@pytest.mark.parametrize("y",
|
|
601
601
|
[cuqi.distribution.Gaussian(np.zeros(6), np.eye(6)),
|
|
602
602
|
cuqi.distribution.Lognormal(np.zeros(6), 4)])
|
|
@@ -17,16 +17,13 @@ ignore_list = [
|
|
|
17
17
|
|
|
18
18
|
# Define cases to skip (these are TODO)
|
|
19
19
|
skip_logd = [
|
|
20
|
-
cuqi.distribution.GMRF, # Mean must allow scalar values
|
|
21
20
|
cuqi.distribution.Gamma # Missing force_ndarray
|
|
22
21
|
]
|
|
23
22
|
skip_sample = [
|
|
24
|
-
cuqi.distribution.GMRF, # Mean must allow scalar values
|
|
25
23
|
cuqi.distribution.Gamma, # Missing force_ndarray
|
|
26
24
|
cuqi.distribution.Lognormal,
|
|
27
25
|
]
|
|
28
26
|
skip_gradient = [
|
|
29
|
-
cuqi.distribution.GMRF, # Mean must allow scalar values
|
|
30
27
|
cuqi.distribution.Gamma, # Missing force_ndarray
|
|
31
28
|
cuqi.distribution.Lognormal,
|
|
32
29
|
cuqi.distribution.InverseGamma,
|
|
@@ -479,6 +479,9 @@ def test_Gibbs_regression(copy_reference):
|
|
|
479
479
|
"data/Gibbs_original_code_results_win.npz")
|
|
480
480
|
samples_orig = np.load(samples_orig_file)
|
|
481
481
|
|
|
482
|
+
# Save results
|
|
483
|
+
#np.savez("Gibbs_original_code_results_win.npz", samples["d"].samples)
|
|
484
|
+
|
|
482
485
|
assert(np.allclose(samples["d"].samples, samples_orig["arr_0"]))
|
|
483
486
|
|
|
484
487
|
def test_Gibbs_continue_sampling():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|