CUQIpy 1.2.0.post0.dev249__tar.gz → 1.2.0.post0.dev294__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-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/CUQIpy.egg-info/PKG-INFO +1 -1
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/PKG-INFO +1 -1
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/_version.py +3 -3
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_laplace_approximation.py +10 -10
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_rto.py +17 -15
- cuqipy-1.2.0.post0.dev294/cuqi/model/__init__.py +1 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/model/_model.py +132 -46
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_laplace_approximation.py +6 -6
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_rto.py +14 -11
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_model.py +43 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_sampler.py +112 -0
- cuqipy-1.2.0.post0.dev249/cuqi/model/__init__.py +0 -1
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/CUQIpy.egg-info/SOURCES.txt +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/CUQIpy.egg-info/dependency_links.txt +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/CUQIpy.egg-info/requires.txt +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/CUQIpy.egg-info/top_level.txt +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/LICENSE +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/README.md +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/_messages.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/array/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/array/_array.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/config.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/data/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/data/_data.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/data/astronaut.npz +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/data/camera.npz +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/data/cat.npz +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/data/cookie.png +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/data/satellite.mat +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/density/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/density/_density.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/diagnostics.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_beta.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_cauchy.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_cmrf.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_custom.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_gamma.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_gaussian.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_gmrf.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_inverse_gamma.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_joint_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_laplace.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_lmrf.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_lognormal.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_modifiedhalfnormal.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_normal.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_posterior.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_smoothed_laplace.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_truncated_normal.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_uniform.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_conjugate.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_conjugate_approx.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_cwmh.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_direct.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_gibbs.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_hmc.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_langevin_algorithm.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_mh.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_pcn.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_sampler.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_utilities.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/geometry/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/geometry/_geometry.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/implicitprior/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/implicitprior/_regularizedGMRF.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/implicitprior/_regularizedGaussian.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/implicitprior/_regularizedUnboundedUniform.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/implicitprior/_restorator.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/likelihood/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/likelihood/_likelihood.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/operator/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/operator/_operator.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/pde/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/pde/_pde.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/problem/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/problem/_problem.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_conjugate.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_conjugate_approx.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_cwmh.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_gibbs.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_hmc.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_langevin_algorithm.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_mh.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_pcn.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_sampler.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/samples/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/samples/_samples.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/solver/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/solver/_solver.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/testproblem/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/testproblem/_testproblem.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/utilities/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/utilities/_get_python_variable_name.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/utilities/_utilities.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/pyproject.toml +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/requirements.txt +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/setup.cfg +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/setup.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_MRFs.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_abstract_distribution_density.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_bayesian_inversion.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_density.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_distributions_shape.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_geometry.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_implicit_priors.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_joint_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_likelihood.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_pde.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_posterior.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_problem.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_samples.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_solver.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_testproblem.py +0 -0
- {cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_utilities.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: CUQIpy
|
|
3
|
-
Version: 1.2.0.post0.
|
|
3
|
+
Version: 1.2.0.post0.dev294
|
|
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: 1.2.0.post0.
|
|
3
|
+
Version: 1.2.0.post0.dev294
|
|
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": "2024-11-
|
|
11
|
+
"date": "2024-11-11T14:11:48+0100",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.2.0.post0.
|
|
14
|
+
"full-revisionid": "231aec2928a511ab48f0c9628b93646ef51c3c8f",
|
|
15
|
+
"version": "1.2.0.post0.dev294"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -74,8 +74,8 @@ class UGLA(Sampler):
|
|
|
74
74
|
return self.target.model
|
|
75
75
|
|
|
76
76
|
@property
|
|
77
|
-
def
|
|
78
|
-
return self.target.data
|
|
77
|
+
def _data(self):
|
|
78
|
+
return self.target.data - self.target.model._shift
|
|
79
79
|
|
|
80
80
|
def _precompute(self):
|
|
81
81
|
|
|
@@ -89,7 +89,7 @@ class UGLA(Sampler):
|
|
|
89
89
|
return W.sqrt() @ D
|
|
90
90
|
self.Lk_fun = Lk_fun
|
|
91
91
|
|
|
92
|
-
self._m = len(self.
|
|
92
|
+
self._m = len(self._data)
|
|
93
93
|
self._L1 = self.likelihood.distribution.sqrtprec
|
|
94
94
|
|
|
95
95
|
# If prior location is scalar, repeat it to match dimensions
|
|
@@ -101,17 +101,17 @@ class UGLA(Sampler):
|
|
|
101
101
|
# Initial Laplace approx
|
|
102
102
|
self._L2 = Lk_fun(self.initial_point)
|
|
103
103
|
self._L2mu = self._L2@self._priorloc
|
|
104
|
-
self._b_tild = np.hstack([self._L1@self.
|
|
104
|
+
self._b_tild = np.hstack([self._L1@self._data, self._L2mu])
|
|
105
105
|
|
|
106
106
|
# Least squares form
|
|
107
107
|
def M(x, flag):
|
|
108
108
|
if flag == 1:
|
|
109
|
-
out1 = self._L1 @ self.model.
|
|
109
|
+
out1 = self._L1 @ self.model._forward_func_no_shift(x) # Use forward function which excludes shift
|
|
110
110
|
out2 = np.sqrt(1/self.prior.scale)*(self._L2 @ x)
|
|
111
111
|
out = np.hstack([out1, out2])
|
|
112
112
|
elif flag == 2:
|
|
113
113
|
idx = int(self._m)
|
|
114
|
-
out1 = self.model.
|
|
114
|
+
out1 = self.model._adjoint_func_no_shift(self._L1.T@x[:idx])
|
|
115
115
|
out2 = np.sqrt(1/self.prior.scale)*(self._L2.T @ x[idx:])
|
|
116
116
|
out = out1 + out2
|
|
117
117
|
return out
|
|
@@ -121,7 +121,7 @@ class UGLA(Sampler):
|
|
|
121
121
|
# Update Laplace approximation
|
|
122
122
|
self._L2 = self.Lk_fun(self.current_point)
|
|
123
123
|
self._L2mu = self._L2@self._priorloc
|
|
124
|
-
self._b_tild = np.hstack([self._L1@self.
|
|
124
|
+
self._b_tild = np.hstack([self._L1@self._data, self._L2mu])
|
|
125
125
|
|
|
126
126
|
# Sample from approximate posterior
|
|
127
127
|
e = np.random.randn(len(self._b_tild))
|
|
@@ -139,9 +139,9 @@ class UGLA(Sampler):
|
|
|
139
139
|
if not isinstance(self.target, cuqi.distribution.Posterior):
|
|
140
140
|
raise ValueError(f"To initialize an object of type {self.__class__}, 'target' need to be of type 'cuqi.distribution.Posterior'.")
|
|
141
141
|
|
|
142
|
-
# Check
|
|
143
|
-
if not isinstance(self.likelihood.model, cuqi.model.
|
|
144
|
-
raise TypeError("Model needs to be linear")
|
|
142
|
+
# Check Affine model
|
|
143
|
+
if not isinstance(self.likelihood.model, cuqi.model.AffineModel):
|
|
144
|
+
raise TypeError("Model needs to be affine or linear")
|
|
145
145
|
|
|
146
146
|
# Check Gaussian likelihood
|
|
147
147
|
if not hasattr(self.likelihood.distribution, "sqrtprec"):
|
|
@@ -11,7 +11,7 @@ class LinearRTO(Sampler):
|
|
|
11
11
|
"""
|
|
12
12
|
Linear RTO (Randomize-Then-Optimize) sampler.
|
|
13
13
|
|
|
14
|
-
Samples posterior related to the inverse problem with Gaussian likelihood and prior, and where the forward model is
|
|
14
|
+
Samples posterior related to the inverse problem with Gaussian likelihood and prior, and where the forward model is linear or more generally affine.
|
|
15
15
|
|
|
16
16
|
Parameters
|
|
17
17
|
------------
|
|
@@ -22,7 +22,7 @@ class LinearRTO(Sampler):
|
|
|
22
22
|
|
|
23
23
|
Here:
|
|
24
24
|
data: is a m-dimensional numpy array containing the measured data.
|
|
25
|
-
model: is a m by n dimensional matrix or LinearModel representing the forward model.
|
|
25
|
+
model: is a m by n dimensional matrix, AffineModel or LinearModel representing the forward model.
|
|
26
26
|
L_sqrtprec: is the squareroot of the precision matrix of the Gaussian likelihood.
|
|
27
27
|
P_mean: is the prior mean.
|
|
28
28
|
P_sqrtprec: is the squareroot of the precision matrix of the Gaussian mean.
|
|
@@ -71,12 +71,15 @@ class LinearRTO(Sampler):
|
|
|
71
71
|
|
|
72
72
|
@property
|
|
73
73
|
def model(self):
|
|
74
|
-
return self.target.model
|
|
75
|
-
|
|
74
|
+
return self.target.model
|
|
75
|
+
|
|
76
76
|
@property
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
def models(self):
|
|
78
|
+
if isinstance(self.target, cuqi.distribution.Posterior):
|
|
79
|
+
return [self.target.model]
|
|
80
|
+
elif isinstance(self.target, cuqi.distribution.MultipleLikelihoodPosterior):
|
|
81
|
+
return self.target.models
|
|
82
|
+
|
|
80
83
|
def _precompute(self):
|
|
81
84
|
L1 = [likelihood.distribution.sqrtprec for likelihood in self.likelihoods]
|
|
82
85
|
L2 = self.prior.sqrtprec
|
|
@@ -84,8 +87,7 @@ class LinearRTO(Sampler):
|
|
|
84
87
|
|
|
85
88
|
# pre-computations
|
|
86
89
|
self.n = self.prior.dim
|
|
87
|
-
self.b_tild = np.hstack([L@likelihood.data for (L, likelihood) in zip(L1, self.likelihoods)]+ [L2mu])
|
|
88
|
-
|
|
90
|
+
self.b_tild = np.hstack([L@(likelihood.data - model._shift) for (L, likelihood, model) in zip(L1, self.likelihoods, self.models)]+ [L2mu]) # With shift from AffineModel
|
|
89
91
|
callability = [callable(likelihood.model) for likelihood in self.likelihoods]
|
|
90
92
|
notcallability = [not c for c in callability]
|
|
91
93
|
if all(notcallability):
|
|
@@ -94,7 +96,7 @@ class LinearRTO(Sampler):
|
|
|
94
96
|
# in this case, model is a function doing forward and backward operations
|
|
95
97
|
def M(x, flag):
|
|
96
98
|
if flag == 1:
|
|
97
|
-
out1 = [L @ likelihood.model.
|
|
99
|
+
out1 = [L @ likelihood.model._forward_func_no_shift(x) for (L, likelihood) in zip(L1, self.likelihoods)] # Use forward function which excludes shift
|
|
98
100
|
out2 = L2 @ x
|
|
99
101
|
out = np.hstack(out1 + [out2])
|
|
100
102
|
elif flag == 2:
|
|
@@ -103,7 +105,7 @@ class LinearRTO(Sampler):
|
|
|
103
105
|
out1 = np.zeros(self.n)
|
|
104
106
|
for likelihood in self.likelihoods:
|
|
105
107
|
idx_end += len(likelihood.data)
|
|
106
|
-
out1 += likelihood.model.
|
|
108
|
+
out1 += likelihood.model._adjoint_func_no_shift(likelihood.distribution.sqrtprec.T@x[idx_start:idx_end])
|
|
107
109
|
idx_start = idx_end
|
|
108
110
|
out2 = L2.T @ x[idx_end:]
|
|
109
111
|
out = out1 + out2
|
|
@@ -129,16 +131,16 @@ class LinearRTO(Sampler):
|
|
|
129
131
|
|
|
130
132
|
# Check Linear model and Gaussian likelihood(s)
|
|
131
133
|
if isinstance(self.target, cuqi.distribution.Posterior):
|
|
132
|
-
if not isinstance(self.model, cuqi.model.
|
|
133
|
-
raise TypeError("Model needs to be linear")
|
|
134
|
+
if not isinstance(self.model, cuqi.model.AffineModel):
|
|
135
|
+
raise TypeError("Model needs to be linear or more generally affine")
|
|
134
136
|
|
|
135
137
|
if not hasattr(self.likelihood.distribution, "sqrtprec"):
|
|
136
138
|
raise TypeError("Distribution in Likelihood must contain a sqrtprec attribute")
|
|
137
139
|
|
|
138
140
|
elif isinstance(self.target, cuqi.distribution.MultipleLikelihoodPosterior): # Elif used for further alternatives, e.g., stacked posterior
|
|
139
141
|
for likelihood in self.likelihoods:
|
|
140
|
-
if not isinstance(likelihood.model, cuqi.model.
|
|
141
|
-
raise TypeError("Model needs to be linear")
|
|
142
|
+
if not isinstance(likelihood.model, cuqi.model.AffineModel):
|
|
143
|
+
raise TypeError("Model needs to be linear or more generally affine")
|
|
142
144
|
|
|
143
145
|
if not hasattr(likelihood.distribution, "sqrtprec"):
|
|
144
146
|
raise TypeError("Distribution in Likelihood must contain a sqrtprec attribute")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from ._model import Model, LinearModel, PDEModel, AffineModel
|
|
@@ -469,8 +469,126 @@ class Model(object):
|
|
|
469
469
|
|
|
470
470
|
def __repr__(self) -> str:
|
|
471
471
|
return "CUQI {}: {} -> {}.\n Forward parameters: {}.".format(self.__class__.__name__,self.domain_geometry,self.range_geometry,cuqi.utilities.get_non_default_args(self))
|
|
472
|
-
|
|
473
|
-
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
class AffineModel(Model):
|
|
475
|
+
""" Model class representing an affine model, i.e. a linear operator with a fixed shift. For linear models, represented by a linear operator only, see :class:`~cuqi.model.LinearModel`.
|
|
476
|
+
|
|
477
|
+
The affine model is defined as:
|
|
478
|
+
|
|
479
|
+
.. math::
|
|
480
|
+
|
|
481
|
+
x \\mapsto Ax + shift
|
|
482
|
+
|
|
483
|
+
where :math:`A` is the linear operator and :math:`shift` is the shift.
|
|
484
|
+
|
|
485
|
+
Parameters
|
|
486
|
+
----------
|
|
487
|
+
|
|
488
|
+
linear_operator : 2d ndarray, callable function or cuqi.model.LinearModel
|
|
489
|
+
The linear operator. If ndarray is given, the operator is assumed to be a matrix.
|
|
490
|
+
|
|
491
|
+
shift : scalar or array_like
|
|
492
|
+
The shift to be added to the forward operator.
|
|
493
|
+
|
|
494
|
+
linear_operator_adjoint : callable function, optional
|
|
495
|
+
The adjoint of the linear operator. Also used for computing gradients.
|
|
496
|
+
|
|
497
|
+
range_geometry : cuqi.geometry.Geometry
|
|
498
|
+
The geometry representing the range.
|
|
499
|
+
|
|
500
|
+
domain_geometry : cuqi.geometry.Geometry
|
|
501
|
+
The geometry representing the domain.
|
|
502
|
+
|
|
503
|
+
"""
|
|
504
|
+
|
|
505
|
+
def __init__(self, linear_operator, shift, linear_operator_adjoint=None, range_geometry=None, domain_geometry=None):
|
|
506
|
+
|
|
507
|
+
# If input represents a matrix, extract needed properties from it
|
|
508
|
+
if hasattr(linear_operator, '__matmul__') and hasattr(linear_operator, 'T'):
|
|
509
|
+
if linear_operator_adjoint is not None:
|
|
510
|
+
raise ValueError("Adjoint of linear operator should not be provided when linear operator is a matrix. If you want to provide an adjoint, use a callable function for the linear operator.")
|
|
511
|
+
|
|
512
|
+
matrix = linear_operator
|
|
513
|
+
|
|
514
|
+
linear_operator = lambda x: matrix@x
|
|
515
|
+
linear_operator_adjoint = lambda y: matrix.T@y
|
|
516
|
+
|
|
517
|
+
if range_geometry is None:
|
|
518
|
+
if hasattr(matrix, 'shape'):
|
|
519
|
+
range_geometry = _DefaultGeometry1D(grid=matrix.shape[0])
|
|
520
|
+
elif isinstance(matrix, LinearModel):
|
|
521
|
+
range_geometry = matrix.range_geometry
|
|
522
|
+
|
|
523
|
+
if domain_geometry is None:
|
|
524
|
+
if hasattr(matrix, 'shape'):
|
|
525
|
+
domain_geometry = _DefaultGeometry1D(grid=matrix.shape[1])
|
|
526
|
+
elif isinstance(matrix, LinearModel):
|
|
527
|
+
domain_geometry = matrix.domain_geometry
|
|
528
|
+
else:
|
|
529
|
+
matrix = None
|
|
530
|
+
|
|
531
|
+
# Ensure that the operators are a callable functions (either provided or created from matrix)
|
|
532
|
+
if not callable(linear_operator):
|
|
533
|
+
raise TypeError("Linear operator must be defined as a matrix or a callable function of some kind")
|
|
534
|
+
if linear_operator_adjoint is not None and not callable(linear_operator_adjoint):
|
|
535
|
+
raise TypeError("Linear operator adjoint must be defined as a callable function of some kind")
|
|
536
|
+
|
|
537
|
+
# Check size of shift and match against range_geometry
|
|
538
|
+
if not np.isscalar(shift):
|
|
539
|
+
if len(shift) != range_geometry.par_dim:
|
|
540
|
+
raise ValueError("The shift should have the same dimension as the range geometry.")
|
|
541
|
+
|
|
542
|
+
# Initialize Model class
|
|
543
|
+
super().__init__(linear_operator, range_geometry, domain_geometry)
|
|
544
|
+
|
|
545
|
+
# Store matrix privately
|
|
546
|
+
self._matrix = matrix
|
|
547
|
+
|
|
548
|
+
# Store shift as private attribute
|
|
549
|
+
self._shift = shift
|
|
550
|
+
|
|
551
|
+
# Store linear operator privately
|
|
552
|
+
self._linear_operator = linear_operator
|
|
553
|
+
|
|
554
|
+
# Store adjoint function
|
|
555
|
+
self._linear_operator_adjoint = linear_operator_adjoint
|
|
556
|
+
|
|
557
|
+
# Define gradient
|
|
558
|
+
self._gradient_func = lambda direction, wrt: linear_operator_adjoint(direction)
|
|
559
|
+
|
|
560
|
+
# Update forward function to include shift (overwriting the one from Model class)
|
|
561
|
+
self._forward_func = lambda *args, **kwargs: linear_operator(*args, **kwargs) + shift
|
|
562
|
+
|
|
563
|
+
# Use arguments from user's callable linear operator (overwriting those found by Model class)
|
|
564
|
+
self._non_default_args = cuqi.utilities.get_non_default_args(linear_operator)
|
|
565
|
+
|
|
566
|
+
@property
|
|
567
|
+
def shift(self):
|
|
568
|
+
""" The shift of the affine model. """
|
|
569
|
+
return self._shift
|
|
570
|
+
|
|
571
|
+
@shift.setter
|
|
572
|
+
def shift(self, value):
|
|
573
|
+
""" Update the shift of the affine model. Updates both the shift value and the underlying forward function. """
|
|
574
|
+
self._shift = value
|
|
575
|
+
self._forward_func = lambda *args, **kwargs: self._linear_operator(*args, **kwargs) + value
|
|
576
|
+
|
|
577
|
+
def _forward_func_no_shift(self, x, is_par=True):
|
|
578
|
+
""" Helper function for computing the forward operator without the shift. """
|
|
579
|
+
return self._apply_func(self._linear_operator,
|
|
580
|
+
self.range_geometry,
|
|
581
|
+
self.domain_geometry,
|
|
582
|
+
x, is_par)
|
|
583
|
+
|
|
584
|
+
def _adjoint_func_no_shift(self, y, is_par=True):
|
|
585
|
+
""" Helper function for computing the adjoint operator without the shift. """
|
|
586
|
+
return self._apply_func(self._linear_operator_adjoint,
|
|
587
|
+
self.domain_geometry,
|
|
588
|
+
self.range_geometry,
|
|
589
|
+
y, is_par)
|
|
590
|
+
|
|
591
|
+
class LinearModel(AffineModel):
|
|
474
592
|
"""Model based on a Linear forward operator.
|
|
475
593
|
|
|
476
594
|
Parameters
|
|
@@ -534,45 +652,11 @@ class LinearModel(Model):
|
|
|
534
652
|
Note that you would need to specify the range and domain geometries in this
|
|
535
653
|
case as they cannot be inferred from the forward and adjoint functions.
|
|
536
654
|
"""
|
|
537
|
-
# Linear forward model with forward and adjoint (transpose).
|
|
538
655
|
|
|
539
|
-
def __init__(self,forward,adjoint=None,range_geometry=None,domain_geometry=None):
|
|
540
|
-
#Assume forward is matrix if not callable (TODO: add more checks)
|
|
541
|
-
if not callable(forward):
|
|
542
|
-
forward_func = lambda x: self._matrix@x
|
|
543
|
-
adjoint_func = lambda y: self._matrix.T@y
|
|
544
|
-
matrix = forward
|
|
545
|
-
else:
|
|
546
|
-
forward_func = forward
|
|
547
|
-
adjoint_func = adjoint
|
|
548
|
-
matrix = None
|
|
549
|
-
|
|
550
|
-
#Check if input is callable
|
|
551
|
-
if callable(adjoint_func) is not True:
|
|
552
|
-
raise TypeError("Adjoint needs to be callable function of some kind")
|
|
553
|
-
|
|
554
|
-
# Use matrix to derive range_geometry and domain_geometry
|
|
555
|
-
if matrix is not None:
|
|
556
|
-
if range_geometry is None:
|
|
557
|
-
range_geometry = _DefaultGeometry1D(grid=matrix.shape[0])
|
|
558
|
-
if domain_geometry is None:
|
|
559
|
-
domain_geometry = _DefaultGeometry1D(grid=matrix.shape[1])
|
|
560
|
-
|
|
561
|
-
#Initialize Model class
|
|
562
|
-
super().__init__(forward_func,range_geometry,domain_geometry)
|
|
563
|
-
|
|
564
|
-
#Add adjoint
|
|
565
|
-
self._adjoint_func = adjoint_func
|
|
566
|
-
|
|
567
|
-
#Store matrix privately
|
|
568
|
-
self._matrix = matrix
|
|
569
|
-
|
|
570
|
-
#Add gradient
|
|
571
|
-
self._gradient_func = lambda direction, wrt: self._adjoint_func(direction)
|
|
656
|
+
def __init__(self, forward, adjoint=None, range_geometry=None, domain_geometry=None):
|
|
572
657
|
|
|
573
|
-
#
|
|
574
|
-
|
|
575
|
-
# assert(self.domain_dim == matrix.shape[1]), "The parameter 'forward' dimensions are inconsistent with parameter 'domain_geometry'"
|
|
658
|
+
#Initialize as AffineModel with shift=0
|
|
659
|
+
super().__init__(forward, 0, adjoint, range_geometry, domain_geometry)
|
|
576
660
|
|
|
577
661
|
def adjoint(self, y, is_par=True):
|
|
578
662
|
""" Adjoint of the model.
|
|
@@ -590,16 +674,21 @@ class LinearModel(Model):
|
|
|
590
674
|
ndarray or cuqi.array.CUQIarray
|
|
591
675
|
The adjoint model output. Always returned as parameters.
|
|
592
676
|
"""
|
|
593
|
-
|
|
677
|
+
if self._linear_operator_adjoint is None:
|
|
678
|
+
raise ValueError("No adjoint operator was provided for this model.")
|
|
679
|
+
return self._apply_func(self._linear_operator_adjoint,
|
|
594
680
|
self.domain_geometry,
|
|
595
681
|
self.range_geometry,
|
|
596
682
|
y, is_par)
|
|
597
683
|
|
|
598
|
-
|
|
684
|
+
def __matmul__(self, x):
|
|
685
|
+
return self.forward(x)
|
|
686
|
+
|
|
599
687
|
def get_matrix(self):
|
|
600
688
|
"""
|
|
601
689
|
Returns an ndarray with the matrix representing the forward operator.
|
|
602
690
|
"""
|
|
691
|
+
|
|
603
692
|
if self._matrix is not None: #Matrix exists so return it
|
|
604
693
|
return self._matrix
|
|
605
694
|
else:
|
|
@@ -617,15 +706,12 @@ class LinearModel(Model):
|
|
|
617
706
|
#Store matrix for future use
|
|
618
707
|
self._matrix = mat
|
|
619
708
|
|
|
620
|
-
return self._matrix
|
|
621
|
-
|
|
622
|
-
def __matmul__(self, x):
|
|
623
|
-
return self.forward(x)
|
|
709
|
+
return self._matrix
|
|
624
710
|
|
|
625
711
|
@property
|
|
626
712
|
def T(self):
|
|
627
713
|
"""Transpose of linear model. Returns a new linear model acting as the transpose."""
|
|
628
|
-
transpose = LinearModel(self.adjoint,self.forward,self.domain_geometry,self.range_geometry)
|
|
714
|
+
transpose = LinearModel(self.adjoint, self.forward, self.domain_geometry, self.range_geometry)
|
|
629
715
|
if self._matrix is not None:
|
|
630
716
|
transpose._matrix = self._matrix.T
|
|
631
717
|
return transpose
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/sampler/_laplace_approximation.py
RENAMED
|
@@ -60,9 +60,9 @@ class UGLA(Sampler):
|
|
|
60
60
|
if not isinstance(self.target, cuqi.distribution.Posterior):
|
|
61
61
|
raise ValueError(f"To initialize an object of type {self.__class__}, 'target' need to be of type 'cuqi.distribution.Posterior'.")
|
|
62
62
|
|
|
63
|
-
# Check
|
|
64
|
-
if not isinstance(self.target.likelihood.model, cuqi.model.
|
|
65
|
-
raise TypeError("Model needs to be linear")
|
|
63
|
+
# Check Affine model
|
|
64
|
+
if not isinstance(self.target.likelihood.model, cuqi.model.AffineModel):
|
|
65
|
+
raise TypeError("Model needs to be affine or linear")
|
|
66
66
|
|
|
67
67
|
# Check Gaussian likelihood
|
|
68
68
|
if not hasattr(self.target.likelihood.distribution, "sqrtprec"):
|
|
@@ -126,7 +126,7 @@ class UGLA(Sampler):
|
|
|
126
126
|
|
|
127
127
|
# Pre-computations
|
|
128
128
|
self._model = self.target.likelihood.model
|
|
129
|
-
self._data = self.target.likelihood.data
|
|
129
|
+
self._data = self.target.likelihood.data - self.target.model._shift
|
|
130
130
|
self._m = len(self._data)
|
|
131
131
|
self._L1 = self.target.likelihood.distribution.sqrtprec
|
|
132
132
|
|
|
@@ -146,12 +146,12 @@ class UGLA(Sampler):
|
|
|
146
146
|
# Least squares form
|
|
147
147
|
def M(x, flag):
|
|
148
148
|
if flag == 1:
|
|
149
|
-
out1 = self._L1 @ self._model.
|
|
149
|
+
out1 = self._L1 @ self._model._forward_func_no_shift(x) # Use forward function which excludes shift
|
|
150
150
|
out2 = np.sqrt(1/self.target.prior.scale)*(self._L2 @ x)
|
|
151
151
|
out = np.hstack([out1, out2])
|
|
152
152
|
elif flag == 2:
|
|
153
153
|
idx = int(self._m)
|
|
154
|
-
out1 = self._model.
|
|
154
|
+
out1 = self._model._adjoint_func_no_shift(self._L1.T@x[:idx])
|
|
155
155
|
out2 = np.sqrt(1/self.target.prior.scale)*(self._L2.T @ x[idx:])
|
|
156
156
|
out = out1 + out2
|
|
157
157
|
return out
|
|
@@ -11,7 +11,7 @@ class LinearRTO(Sampler):
|
|
|
11
11
|
"""
|
|
12
12
|
Linear RTO (Randomize-Then-Optimize) sampler.
|
|
13
13
|
|
|
14
|
-
Samples posterior related to the inverse problem with Gaussian likelihood and prior, and where the forward model is
|
|
14
|
+
Samples posterior related to the inverse problem with Gaussian likelihood and prior, and where the forward model is linear or more generally affine.
|
|
15
15
|
|
|
16
16
|
Parameters
|
|
17
17
|
------------
|
|
@@ -22,7 +22,7 @@ class LinearRTO(Sampler):
|
|
|
22
22
|
|
|
23
23
|
Here:
|
|
24
24
|
data: is a m-dimensional numpy array containing the measured data.
|
|
25
|
-
model: is a m by n dimensional matrix or LinearModel representing the forward model.
|
|
25
|
+
model: is a m by n dimensional matrix, AffineModel or LinearModel representing the forward model.
|
|
26
26
|
L_sqrtprec: is the squareroot of the precision matrix of the Gaussian likelihood.
|
|
27
27
|
P_mean: is the prior mean.
|
|
28
28
|
P_sqrtprec: is the squareroot of the precision matrix of the Gaussian mean.
|
|
@@ -59,8 +59,8 @@ class LinearRTO(Sampler):
|
|
|
59
59
|
model = cuqi.model.LinearModel(model)
|
|
60
60
|
|
|
61
61
|
# Check model input
|
|
62
|
-
if not isinstance(model, cuqi.model.
|
|
63
|
-
raise TypeError("Model needs to be cuqi.model.
|
|
62
|
+
if not isinstance(model, cuqi.model.AffineModel):
|
|
63
|
+
raise TypeError("Model needs to be cuqi.model.AffineModel or matrix")
|
|
64
64
|
|
|
65
65
|
# Likelihood
|
|
66
66
|
L = cuqi.distribution.Gaussian(model, sqrtprec=L_sqrtprec).to_likelihood(data)
|
|
@@ -95,7 +95,7 @@ class LinearRTO(Sampler):
|
|
|
95
95
|
|
|
96
96
|
# pre-computations
|
|
97
97
|
self.n = len(self.x0)
|
|
98
|
-
self.b_tild = np.hstack([L@likelihood.data for (L, likelihood) in zip(L1, self.likelihoods)]+ [L2mu])
|
|
98
|
+
self.b_tild = np.hstack([L@(likelihood.data - model._shift) for (L, likelihood, model) in zip(L1, self.likelihoods, self.models)]+ [L2mu])
|
|
99
99
|
|
|
100
100
|
callability = [callable(likelihood.model) for likelihood in self.likelihoods]
|
|
101
101
|
notcallability = [not c for c in callability]
|
|
@@ -105,7 +105,7 @@ class LinearRTO(Sampler):
|
|
|
105
105
|
# in this case, model is a function doing forward and backward operations
|
|
106
106
|
def M(x, flag):
|
|
107
107
|
if flag == 1:
|
|
108
|
-
out1 = [L @ likelihood.model.
|
|
108
|
+
out1 = [L @ likelihood.model._forward_func_no_shift(x) for (L, likelihood) in zip(L1, self.likelihoods)] # Use forward function which excludes shift
|
|
109
109
|
out2 = L2 @ x
|
|
110
110
|
out = np.hstack(out1 + [out2])
|
|
111
111
|
elif flag == 2:
|
|
@@ -114,7 +114,7 @@ class LinearRTO(Sampler):
|
|
|
114
114
|
out1 = np.zeros(self.n)
|
|
115
115
|
for likelihood in self.likelihoods:
|
|
116
116
|
idx_end += len(likelihood.data)
|
|
117
|
-
out1 += likelihood.model.
|
|
117
|
+
out1 += likelihood.model._adjoint_func_no_shift(likelihood.distribution.sqrtprec.T@x[idx_start:idx_end]) # Use adjoint function which excludes shift
|
|
118
118
|
idx_start = idx_end
|
|
119
119
|
out2 = L2.T @ x[idx_end:]
|
|
120
120
|
out = out1 + out2
|
|
@@ -143,8 +143,11 @@ class LinearRTO(Sampler):
|
|
|
143
143
|
return self.target.model
|
|
144
144
|
|
|
145
145
|
@property
|
|
146
|
-
def
|
|
147
|
-
|
|
146
|
+
def models(self):
|
|
147
|
+
if isinstance(self.target, cuqi.distribution.Posterior):
|
|
148
|
+
return [self.target.model]
|
|
149
|
+
elif isinstance(self.target, cuqi.distribution.MultipleLikelihoodPosterior):
|
|
150
|
+
return self.target.models
|
|
148
151
|
|
|
149
152
|
def _sample(self, N, Nb):
|
|
150
153
|
Ns = N+Nb # number of simulations
|
|
@@ -175,8 +178,8 @@ class LinearRTO(Sampler):
|
|
|
175
178
|
|
|
176
179
|
# Check Linear model and Gaussian likelihood(s)
|
|
177
180
|
if isinstance(self.target, cuqi.distribution.Posterior):
|
|
178
|
-
if not isinstance(self.model, cuqi.model.
|
|
179
|
-
raise TypeError("Model needs to be linear")
|
|
181
|
+
if not isinstance(self.model, cuqi.model.AffineModel):
|
|
182
|
+
raise TypeError("Model needs to be linear or affine")
|
|
180
183
|
|
|
181
184
|
if not hasattr(self.likelihood.distribution, "sqrtprec"):
|
|
182
185
|
raise TypeError("Distribution in Likelihood must contain a sqrtprec attribute")
|
|
@@ -5,6 +5,7 @@ import scipy as sp
|
|
|
5
5
|
import cuqi
|
|
6
6
|
import pytest
|
|
7
7
|
from scipy import optimize
|
|
8
|
+
from copy import copy
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@pytest.mark.parametrize("seed",[(0),(1),(2)])
|
|
@@ -471,3 +472,45 @@ def test_model_allows_jacobian_or_gradient():
|
|
|
471
472
|
|
|
472
473
|
assert np.allclose(model_grad.gradient(dir, wrt), model_jac.gradient(dir, wrt))
|
|
473
474
|
|
|
475
|
+
# Parametrize over models
|
|
476
|
+
@pytest.mark.parametrize("model", [cuqi.testproblem.Deconvolution1D().model,
|
|
477
|
+
cuqi.testproblem.Heat1D().model])
|
|
478
|
+
def test_AffineModel_Correct_result(model):
|
|
479
|
+
""" Test creating a shifted linear model from a linear model """
|
|
480
|
+
|
|
481
|
+
# Random vectors
|
|
482
|
+
x = np.random.randn(model.domain_dim)
|
|
483
|
+
b = np.random.randn(model.range_dim)
|
|
484
|
+
|
|
485
|
+
A_affine = cuqi.model.AffineModel(model, b, range_geometry = model.range_geometry, domain_geometry = model.domain_geometry)
|
|
486
|
+
|
|
487
|
+
# Dimension check
|
|
488
|
+
assert A_affine.range_dim == model.range_dim
|
|
489
|
+
|
|
490
|
+
# Check that the shifted linear model is correct
|
|
491
|
+
assert np.allclose(A_affine(x), model(x) + b)
|
|
492
|
+
|
|
493
|
+
def test_AffineModel_update_shift():
|
|
494
|
+
|
|
495
|
+
A = np.eye(2)
|
|
496
|
+
b = np.array([1, 2])
|
|
497
|
+
x = np.array([1, 1])
|
|
498
|
+
new_shift = np.array([2,-1])
|
|
499
|
+
model = cuqi.model.AffineModel(A, b)
|
|
500
|
+
model_copy = copy(model)
|
|
501
|
+
|
|
502
|
+
# check model output
|
|
503
|
+
assert np.all(model(x) == np.array([2,3]))
|
|
504
|
+
|
|
505
|
+
# check model output with updated shift
|
|
506
|
+
model.shift = new_shift
|
|
507
|
+
assert np.all(model(x) == np.array([3,0]))
|
|
508
|
+
|
|
509
|
+
# check model output of copied model
|
|
510
|
+
assert np.all(model_copy(x) == np.array([2,3]))
|
|
511
|
+
|
|
512
|
+
# check model output of copied model with updated shift
|
|
513
|
+
model_copy.shift = new_shift
|
|
514
|
+
assert np.all(model_copy(x) == np.array([3,0]))
|
|
515
|
+
|
|
516
|
+
|
|
@@ -510,3 +510,115 @@ def test_Gibbs_geometry_matches():
|
|
|
510
510
|
assert samples["d"].geometry == target.get_density("d").geometry
|
|
511
511
|
assert samples["l"].geometry == target.get_density("l").geometry
|
|
512
512
|
assert samples["x"].geometry == target.get_density("x").geometry
|
|
513
|
+
|
|
514
|
+
def test_RTO_with_AffineModel_is_equivalent_to_LinearModel_and_shifted_data():
|
|
515
|
+
# Define LinearModel and data
|
|
516
|
+
A, y_obs, _ = cuqi.testproblem.Deconvolution1D().get_components()
|
|
517
|
+
|
|
518
|
+
# Define Shift
|
|
519
|
+
shift = np.random.rand(A.domain_dim)
|
|
520
|
+
|
|
521
|
+
# Define Bayesian Problem
|
|
522
|
+
x = cuqi.distribution.GMRF(np.zeros(A.domain_dim), 100)
|
|
523
|
+
y = cuqi.distribution.Gaussian(A@x, 0.01**2)
|
|
524
|
+
posterior = cuqi.distribution.JointDistribution(x, y)(y=y_obs-shift)
|
|
525
|
+
|
|
526
|
+
# Set up LinearRTO with both models
|
|
527
|
+
sampler_linear = cuqi.sampler.LinearRTO(posterior)
|
|
528
|
+
|
|
529
|
+
# Sample with fixes seed
|
|
530
|
+
np.random.seed(0)
|
|
531
|
+
samples_linear = sampler_linear.sample(10, 2)
|
|
532
|
+
|
|
533
|
+
# Define AffineModel
|
|
534
|
+
affine_model = cuqi.model.AffineModel(A, shift)
|
|
535
|
+
|
|
536
|
+
# Set up LinearRTO with AffineModel
|
|
537
|
+
y = cuqi.distribution.Gaussian(affine_model, 0.01**2)
|
|
538
|
+
posterior_affine = cuqi.distribution.JointDistribution(x, y)(y=y_obs)
|
|
539
|
+
|
|
540
|
+
# Set up LinearRTO with AffineModel
|
|
541
|
+
sampler_affine = cuqi.sampler.LinearRTO(posterior_affine)
|
|
542
|
+
|
|
543
|
+
# Sample with fixes seed
|
|
544
|
+
np.random.seed(0)
|
|
545
|
+
samples_affine = sampler_affine.sample(10, 2)
|
|
546
|
+
|
|
547
|
+
# Check that the samples are the same
|
|
548
|
+
assert np.allclose(samples_linear.samples, samples_affine.samples)
|
|
549
|
+
|
|
550
|
+
def test_RegularizedRTO_with_AffieModel_is_equivalent_to_LinearModel_and_shifted_data():
|
|
551
|
+
""" Test that sampling with RegularizedRTO with an AffineModel is equivalent to sampling with LinearModel and shifted data. """
|
|
552
|
+
|
|
553
|
+
# Define LinearModel and data
|
|
554
|
+
A, y_obs, _ = cuqi.testproblem.Deconvolution1D().get_components()
|
|
555
|
+
|
|
556
|
+
# Define Shift
|
|
557
|
+
shift = np.random.rand(A.domain_dim)
|
|
558
|
+
|
|
559
|
+
# Define Bayesian Problem
|
|
560
|
+
x = cuqi.implicitprior.NonnegativeGMRF(np.zeros(A.domain_dim), 100)
|
|
561
|
+
y = cuqi.distribution.Gaussian(A@x, 0.01**2)
|
|
562
|
+
posterior = cuqi.distribution.JointDistribution(x, y)(y=y_obs-shift)
|
|
563
|
+
|
|
564
|
+
# Set up RegularizedRTO with both models
|
|
565
|
+
sampler_linear = cuqi.sampler.RegularizedLinearRTO(posterior)
|
|
566
|
+
|
|
567
|
+
# Sample with fixes seed
|
|
568
|
+
np.random.seed(0)
|
|
569
|
+
samples_linear = sampler_linear.sample(10, 2)
|
|
570
|
+
|
|
571
|
+
# Define AffineModel
|
|
572
|
+
affine_model = cuqi.model.AffineModel(A, shift)
|
|
573
|
+
|
|
574
|
+
# Set up RegularizedRTO with AffineModel
|
|
575
|
+
y = cuqi.distribution.Gaussian(affine_model, 0.01**2)
|
|
576
|
+
posterior_affine = cuqi.distribution.JointDistribution(x, y)(y=y_obs)
|
|
577
|
+
|
|
578
|
+
# Set up RegularizedRTO with AffineModel
|
|
579
|
+
sampler_affine = cuqi.sampler.RegularizedLinearRTO(posterior_affine)
|
|
580
|
+
|
|
581
|
+
# Sample with fixes seed
|
|
582
|
+
np.random.seed(0)
|
|
583
|
+
samples_affine = sampler_affine.sample(10, 2)
|
|
584
|
+
|
|
585
|
+
# Check that the samples are the same
|
|
586
|
+
assert np.allclose(samples_linear.samples, samples_affine.samples)
|
|
587
|
+
|
|
588
|
+
def test_UGLA_with_AffineModel_is_equivalent_to_LinearModel_and_shifted_data():
|
|
589
|
+
""" Test that sampling with UGLA with an AffineModel is equivalent to sampling with LinearModel and shifted data. """
|
|
590
|
+
|
|
591
|
+
# Define LinearModel and data
|
|
592
|
+
A, y_obs, _ = cuqi.testproblem.Deconvolution1D().get_components()
|
|
593
|
+
|
|
594
|
+
# Define Shift
|
|
595
|
+
shift = np.random.rand(A.domain_dim)
|
|
596
|
+
|
|
597
|
+
# Define Bayesian Problem
|
|
598
|
+
x = cuqi.distribution.LMRF(np.zeros(A.domain_dim), 0.01)
|
|
599
|
+
y = cuqi.distribution.Gaussian(A@x, 0.01**2)
|
|
600
|
+
posterior = cuqi.distribution.JointDistribution(x, y)(y=y_obs-shift)
|
|
601
|
+
|
|
602
|
+
# Set up UGLA with both models
|
|
603
|
+
sampler_linear = cuqi.sampler.UGLA(posterior)
|
|
604
|
+
|
|
605
|
+
# Sample with fixes seed
|
|
606
|
+
np.random.seed(0)
|
|
607
|
+
samples_linear = sampler_linear.sample(10, 2)
|
|
608
|
+
|
|
609
|
+
# Define AffineModel
|
|
610
|
+
affine_model = cuqi.model.AffineModel(A, shift)
|
|
611
|
+
|
|
612
|
+
# Set up UGLA with AffineModel
|
|
613
|
+
y = cuqi.distribution.Gaussian(affine_model, 0.01**2)
|
|
614
|
+
posterior_affine = cuqi.distribution.JointDistribution(x, y)(y=y_obs)
|
|
615
|
+
|
|
616
|
+
# Set up UGLA with AffineModel
|
|
617
|
+
sampler_affine = cuqi.sampler.UGLA(posterior_affine)
|
|
618
|
+
|
|
619
|
+
# Sample with fixes seed
|
|
620
|
+
np.random.seed(0)
|
|
621
|
+
samples_affine = sampler_affine.sample(10, 2)
|
|
622
|
+
|
|
623
|
+
# Check that the samples are the same
|
|
624
|
+
assert np.allclose(samples_linear.samples, samples_affine.samples)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from ._model import Model, LinearModel, PDEModel
|
|
File without changes
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/CUQIpy.egg-info/dependency_links.txt
RENAMED
|
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
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_joint_distribution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_modifiedhalfnormal.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_smoothed_laplace.py
RENAMED
|
File without changes
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/distribution/_truncated_normal.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_conjugate.py
RENAMED
|
File without changes
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_conjugate_approx.py
RENAMED
|
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
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/experimental/mcmc/_utilities.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/implicitprior/_regularizedGMRF.py
RENAMED
|
File without changes
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/implicitprior/_regularizedGaussian.py
RENAMED
|
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
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/cuqi/utilities/_get_python_variable_name.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuqipy-1.2.0.post0.dev249 → cuqipy-1.2.0.post0.dev294}/tests/test_abstract_distribution_density.py
RENAMED
|
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
|