CUQIpy 1.2.0.post0.dev90__tar.gz → 1.2.0.post0.dev109__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.dev90 → cuqipy-1.2.0.post0.dev109}/CUQIpy.egg-info/PKG-INFO +1 -1
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/PKG-INFO +1 -1
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/_version.py +3 -3
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_rto.py +2 -2
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_rto.py +2 -2
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/solver/__init__.py +1 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/solver/_solver.py +169 -4
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_solver.py +47 -3
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/CUQIpy.egg-info/SOURCES.txt +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/CUQIpy.egg-info/dependency_links.txt +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/CUQIpy.egg-info/requires.txt +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/CUQIpy.egg-info/top_level.txt +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/LICENSE +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/README.md +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/_messages.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/array/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/array/_array.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/config.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/data/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/data/_data.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/data/astronaut.npz +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/data/camera.npz +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/data/cat.npz +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/data/cookie.png +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/data/satellite.mat +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/density/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/density/_density.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/diagnostics.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_beta.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_cauchy.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_cmrf.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_custom.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_gamma.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_gaussian.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_gmrf.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_inverse_gamma.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_joint_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_laplace.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_lmrf.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_lognormal.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_modifiedhalfnormal.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_normal.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_posterior.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_smoothed_laplace.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_truncated_normal.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_uniform.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_conjugate.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_conjugate_approx.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_cwmh.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_direct.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_gibbs.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_hmc.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_langevin_algorithm.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_laplace_approximation.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_mh.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_pcn.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_sampler.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_utilities.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/geometry/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/geometry/_geometry.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/implicitprior/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/implicitprior/_regularizedGMRF.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/implicitprior/_regularizedGaussian.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/implicitprior/_regularizedUnboundedUniform.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/likelihood/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/likelihood/_likelihood.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/model/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/model/_model.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/operator/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/operator/_operator.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/pde/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/pde/_pde.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/problem/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/problem/_problem.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_conjugate.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_conjugate_approx.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_cwmh.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_gibbs.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_hmc.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_langevin_algorithm.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_laplace_approximation.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_mh.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_pcn.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_sampler.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/samples/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/samples/_samples.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/testproblem/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/testproblem/_testproblem.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/utilities/__init__.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/utilities/_get_python_variable_name.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/utilities/_utilities.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/pyproject.toml +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/requirements.txt +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/setup.cfg +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/setup.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_MRFs.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_abstract_distribution_density.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_bayesian_inversion.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_density.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_distributions_shape.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_geometry.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_implicit_priors.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_joint_distribution.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_likelihood.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_model.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_pde.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_posterior.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_problem.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_sampler.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_samples.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/tests/test_testproblem.py +0 -0
- {cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/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.dev109
|
|
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.dev109
|
|
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-08T11:08:22+0100",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "1.2.0.post0.
|
|
14
|
+
"full-revisionid": "17570092caf729244ade9c6d647cfa1d2b9ef5f0",
|
|
15
|
+
"version": "1.2.0.post0.dev109"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -235,8 +235,8 @@ class RegularizedLinearRTO(LinearRTO):
|
|
|
235
235
|
|
|
236
236
|
def step(self):
|
|
237
237
|
y = self.b_tild + np.random.randn(len(self.b_tild))
|
|
238
|
-
sim = FISTA(self.M, y, self.
|
|
239
|
-
maxit = self.maxit, stepsize = self._stepsize, abstol = self.abstol, adaptive = self.adaptive)
|
|
238
|
+
sim = FISTA(self.M, y, self.proximal,
|
|
239
|
+
self.current_point, maxit = self.maxit, stepsize = self._stepsize, abstol = self.abstol, adaptive = self.adaptive)
|
|
240
240
|
self.current_point, _ = sim.solve()
|
|
241
241
|
acc = 1
|
|
242
242
|
return acc
|
|
@@ -267,8 +267,8 @@ class RegularizedLinearRTO(LinearRTO):
|
|
|
267
267
|
samples[:, 0] = self.x0
|
|
268
268
|
for s in range(Ns-1):
|
|
269
269
|
y = self.b_tild + np.random.randn(len(self.b_tild))
|
|
270
|
-
sim = FISTA(self.M, y,
|
|
271
|
-
maxit = self.maxit, stepsize = _stepsize, abstol = self.abstol, adaptive = self.adaptive)
|
|
270
|
+
sim = FISTA(self.M, y, self.proximal,
|
|
271
|
+
samples[:, s], maxit = self.maxit, stepsize = _stepsize, abstol = self.abstol, adaptive = self.adaptive)
|
|
272
272
|
samples[:, s+1], _ = sim.solve()
|
|
273
273
|
|
|
274
274
|
self._print_progress(s+2,Ns) #s+2 is the sample number, s+1 is index assuming x0 is the first sample
|
|
@@ -584,8 +584,8 @@ class FISTA(object):
|
|
|
584
584
|
----------
|
|
585
585
|
A : ndarray or callable f(x,*args).
|
|
586
586
|
b : ndarray.
|
|
587
|
-
x0 : ndarray. Initial guess.
|
|
588
587
|
proximal : callable f(x, gamma) for proximal mapping.
|
|
588
|
+
x0 : ndarray. Initial guess.
|
|
589
589
|
maxit : The maximum number of iterations.
|
|
590
590
|
stepsize : The stepsize of the gradient step.
|
|
591
591
|
abstol : The numerical tolerance for convergence checks.
|
|
@@ -606,11 +606,11 @@ class FISTA(object):
|
|
|
606
606
|
b = rng.standard_normal(m)
|
|
607
607
|
stepsize = 0.99/(sp.linalg.interpolative.estimate_spectral_norm(A)**2)
|
|
608
608
|
x0 = np.zeros(n)
|
|
609
|
-
fista = FISTA(A, b,
|
|
609
|
+
fista = FISTA(A, b, proximal = ProximalL1, x0, stepsize = stepsize, maxit = 100, abstol=1e-12, adaptive = True)
|
|
610
610
|
sol, _ = fista.solve()
|
|
611
611
|
|
|
612
612
|
"""
|
|
613
|
-
def __init__(self, A, b,
|
|
613
|
+
def __init__(self, A, b, proximal, x0, maxit=100, stepsize=1e0, abstol=1e-14, adaptive = True):
|
|
614
614
|
|
|
615
615
|
self.A = A
|
|
616
616
|
self.b = b
|
|
@@ -650,8 +650,157 @@ class FISTA(object):
|
|
|
650
650
|
x_new = x_new + ((k-1)/(k+2))*(x_new - x_old)
|
|
651
651
|
|
|
652
652
|
x = x_new.copy()
|
|
653
|
+
|
|
654
|
+
class ADMM(object):
|
|
655
|
+
"""Alternating Direction Method of Multipliers for solving regularized linear least squares problems of the form:
|
|
656
|
+
Minimize ||Ax-b||^2 + sum_i f_i(L_i x),
|
|
657
|
+
where the sum ranges from 1 to an arbitrary n. See definition of the parameter `penalty_terms` below for more details about f_i and L_i
|
|
658
|
+
|
|
659
|
+
Reference:
|
|
660
|
+
[1] Boyd et al. "Distributed optimization and statistical learning via the alternating direction method of multipliers."Foundations and Trends® in Machine learning, 2011.
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
Parameters
|
|
664
|
+
----------
|
|
665
|
+
A : ndarray or callable
|
|
666
|
+
Represents a matrix or a function that performs matrix-vector multiplications.
|
|
667
|
+
When A is a callable, it accepts arguments (x, flag) where:
|
|
668
|
+
- flag=1 indicates multiplication of A with vector x, that is A @ x.
|
|
669
|
+
- flag=2 indicates multiplication of the transpose of A with vector x, that is A.T @ x.
|
|
670
|
+
b : ndarray.
|
|
671
|
+
penalty_terms : List of tuples (callable proximal operator of f_i, linear operator L_i)
|
|
672
|
+
Each callable proximal operator f_i accepts two arguments (x, p) and should return the minimizer of p/2||x-z||^2 + f(x) over z for some f.
|
|
673
|
+
x0 : ndarray. Initial guess.
|
|
674
|
+
penalty_parameter : Trade-off between linear least squares and regularization term in the solver iterates. Denoted as "rho" in [1].
|
|
675
|
+
maxit : The maximum number of iterations.
|
|
676
|
+
adaptive : Whether to adaptively update the penalty_parameter each iteration such that the primal and dual residual norms are of the same order of magnitude. Based on [1], Subsection 3.4.1
|
|
677
|
+
|
|
678
|
+
Example
|
|
679
|
+
-----------
|
|
680
|
+
.. code-block:: python
|
|
653
681
|
|
|
682
|
+
from cuqi.solver import ADMM, ProximalL1, ProjectNonnegative
|
|
683
|
+
import numpy as np
|
|
684
|
+
|
|
685
|
+
rng = np.random.default_rng()
|
|
686
|
+
|
|
687
|
+
m, n, k = 10, 5, 4
|
|
688
|
+
A = rng.standard_normal((m, n))
|
|
689
|
+
b = rng.standard_normal(m)
|
|
690
|
+
L = rng.standard_normal((k, n))
|
|
691
|
+
|
|
692
|
+
x0 = np.zeros(n)
|
|
693
|
+
admm = ADMM(A, b, x0, penalty_terms = [(ProximalL1, L), (lambda z, _ : ProjectNonnegative(z), np.eye(n))], tradeoff = 10)
|
|
694
|
+
sol, _ = admm.solve()
|
|
695
|
+
|
|
696
|
+
"""
|
|
697
|
+
|
|
698
|
+
def __init__(self, A, b, penalty_terms, x0, penalty_parameter = 10, maxit = 100, inner_max_it = 10, adaptive = True):
|
|
699
|
+
|
|
700
|
+
self.A = A
|
|
701
|
+
self.b = b
|
|
702
|
+
self.x_cur = x0
|
|
703
|
+
|
|
704
|
+
dual_len = [penalty[1].shape[0] for penalty in penalty_terms]
|
|
705
|
+
self.z_cur = [np.zeros(l) for l in dual_len]
|
|
706
|
+
self.u_cur = [np.zeros(l) for l in dual_len]
|
|
707
|
+
self.n = penalty_terms[0][1].shape[1]
|
|
708
|
+
|
|
709
|
+
self.rho = penalty_parameter
|
|
710
|
+
self.maxit = maxit
|
|
711
|
+
self.inner_max_it = inner_max_it
|
|
712
|
+
self.adaptive = adaptive
|
|
713
|
+
|
|
714
|
+
self.penalty_terms = penalty_terms
|
|
715
|
+
|
|
716
|
+
self.p = len(self.penalty_terms)
|
|
717
|
+
self._big_matrix = None
|
|
718
|
+
self._big_vector = None
|
|
719
|
+
|
|
720
|
+
def solve(self):
|
|
721
|
+
"""
|
|
722
|
+
Solves the regularized linear least squares problem using ADMM in scaled form. Based on [1], Subsection 3.1.1
|
|
723
|
+
"""
|
|
724
|
+
z_new = self.p*[0]
|
|
725
|
+
u_new = self.p*[0]
|
|
726
|
+
|
|
727
|
+
# Iterating
|
|
728
|
+
for i in range(self.maxit):
|
|
729
|
+
self._iteration_pre_processing()
|
|
730
|
+
|
|
731
|
+
# Main update (Least Squares)
|
|
732
|
+
solver = CGLS(self._big_matrix, self._big_vector, self.x_cur, self.inner_max_it)
|
|
733
|
+
x_new, _ = solver.solve()
|
|
734
|
+
|
|
735
|
+
# Regularization update
|
|
736
|
+
for j, penalty in enumerate(self.penalty_terms):
|
|
737
|
+
z_new[j] = penalty[0](penalty[1]@x_new + self.u_cur[j], 1.0/self.rho)
|
|
738
|
+
|
|
739
|
+
res_primal = 0.0
|
|
740
|
+
# Dual update
|
|
741
|
+
for j, penalty in enumerate(self.penalty_terms):
|
|
742
|
+
r_partial = penalty[1]@x_new - z_new[j]
|
|
743
|
+
res_primal += LA.norm(r_partial)**2
|
|
744
|
+
|
|
745
|
+
u_new[j] = self.u_cur[j] + r_partial
|
|
746
|
+
|
|
747
|
+
res_dual = 0.0
|
|
748
|
+
for j, penalty in enumerate(self.penalty_terms):
|
|
749
|
+
res_dual += LA.norm(penalty[1].T@(z_new[j] - self.z_cur[j]))**2
|
|
750
|
+
|
|
751
|
+
# Adaptive approach based on [1], Subsection 3.4.1
|
|
752
|
+
if self.adaptive:
|
|
753
|
+
if res_dual > 1e2*res_primal:
|
|
754
|
+
self.rho *= 0.5 # More regularization
|
|
755
|
+
elif res_primal > 1e2*res_dual:
|
|
756
|
+
self.rho *= 2.0 # More data fidelity
|
|
757
|
+
|
|
758
|
+
self.x_cur, self.z_cur, self.u_cur = x_new, z_new.copy(), u_new
|
|
759
|
+
|
|
760
|
+
return self.x_cur, i
|
|
654
761
|
|
|
762
|
+
def _iteration_pre_processing(self):
|
|
763
|
+
""" Preprocessing
|
|
764
|
+
Every iteration of ADMM requires solving a linear least squares system of the form
|
|
765
|
+
minimize 1/(rho) \|Ax-b\|_2^2 + sum_{i=1}^{p} \|penalty[1]x - (y - u)\|_2^2
|
|
766
|
+
To solve this, all linear least squares terms are combined into a single big term
|
|
767
|
+
with matrix big_matrix and data big_vector.
|
|
768
|
+
|
|
769
|
+
The matrix only needs to be updated when rho changes, i.e., when the adaptive option is used.
|
|
770
|
+
The data vector needs to be updated every iteration.
|
|
771
|
+
"""
|
|
772
|
+
|
|
773
|
+
self._big_vector = np.hstack([np.sqrt(1/self.rho)*self.b] + [self.z_cur[i] - self.u_cur[i] for i in range(self.p)])
|
|
774
|
+
|
|
775
|
+
# Check whether matrix needs to be updated
|
|
776
|
+
if self._big_matrix is not None and not self.adaptive:
|
|
777
|
+
return
|
|
778
|
+
|
|
779
|
+
# Update big_matrix
|
|
780
|
+
if callable(self.A):
|
|
781
|
+
def matrix_eval(x, flag):
|
|
782
|
+
if flag == 1:
|
|
783
|
+
out1 = np.sqrt(1/self.rho)*self.A(x, 1)
|
|
784
|
+
out2 = [penalty[1]@x for penalty in self.penalty_terms]
|
|
785
|
+
out = np.hstack([out1] + out2)
|
|
786
|
+
elif flag == 2:
|
|
787
|
+
idx_start = len(x)
|
|
788
|
+
idx_end = len(x)
|
|
789
|
+
out1 = np.zeros(self.n)
|
|
790
|
+
for _, t in reversed(self.penalty_terms):
|
|
791
|
+
idx_start -= t.shape[0]
|
|
792
|
+
out1 += t.T@x[idx_start:idx_end]
|
|
793
|
+
idx_end = idx_start
|
|
794
|
+
out2 = np.sqrt(1/self.rho)*self.A(x[:idx_end], 2)
|
|
795
|
+
out = out1 + out2
|
|
796
|
+
return out
|
|
797
|
+
self._big_matrix = matrix_eval
|
|
798
|
+
else:
|
|
799
|
+
self._big_matrix = np.vstack([np.sqrt(1/self.rho)*self.A] + [penalty[1] for penalty in self.penalty_terms])
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
|
|
655
804
|
def ProjectNonnegative(x):
|
|
656
805
|
"""(Euclidean) projection onto the nonnegative orthant.
|
|
657
806
|
|
|
@@ -678,6 +827,22 @@ def ProjectBox(x, lower = None, upper = None):
|
|
|
678
827
|
|
|
679
828
|
return np.minimum(np.maximum(x, lower), upper)
|
|
680
829
|
|
|
830
|
+
def ProjectHalfspace(x, a, b):
|
|
831
|
+
"""(Euclidean) projection onto the halfspace defined {z|<a,z> <= b}.
|
|
832
|
+
|
|
833
|
+
Parameters
|
|
834
|
+
----------
|
|
835
|
+
x : array_like.
|
|
836
|
+
a : array_like.
|
|
837
|
+
b : array_like.
|
|
838
|
+
"""
|
|
839
|
+
|
|
840
|
+
ax_b = np.inner(a,x) - b
|
|
841
|
+
if ax_b <= 0:
|
|
842
|
+
return x
|
|
843
|
+
else:
|
|
844
|
+
return x - (ax_b/np.inner(a,a))*a
|
|
845
|
+
|
|
681
846
|
def ProximalL1(x, gamma):
|
|
682
847
|
"""(Euclidean) proximal operator of the \|x\|_1 norm.
|
|
683
848
|
Also known as the shrinkage or soft thresholding operator.
|
|
@@ -687,4 +852,4 @@ def ProximalL1(x, gamma):
|
|
|
687
852
|
x : array_like.
|
|
688
853
|
gamma : scale parameter.
|
|
689
854
|
"""
|
|
690
|
-
return np.multiply(np.sign(x), np.maximum(np.abs(x)-gamma, 0))
|
|
855
|
+
return np.multiply(np.sign(x), np.maximum(np.abs(x)-gamma, 0))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import scipy as sp
|
|
3
3
|
|
|
4
|
-
from cuqi.solver import CGLS, LM, FISTA, ProximalL1
|
|
4
|
+
from cuqi.solver import CGLS, LM, FISTA, ADMM, ProximalL1, ProjectNonnegative
|
|
5
5
|
from scipy.optimize import lsq_linear
|
|
6
6
|
|
|
7
7
|
|
|
@@ -54,8 +54,52 @@ def test_FISTA():
|
|
|
54
54
|
|
|
55
55
|
stepsize = 0.99/(sp.linalg.interpolative.estimate_spectral_norm(A)**2)
|
|
56
56
|
x0 = np.zeros(n)
|
|
57
|
-
sol, _ = FISTA(A, b,
|
|
57
|
+
sol, _ = FISTA(A, b, ProximalL1, x0, stepsize = stepsize, maxit = 100, abstol=1e-12, adaptive = True).solve()
|
|
58
58
|
|
|
59
59
|
ref_sol = np.array([-1.83273787e-03, -1.72094582e-13, 0.0, -3.35835639e-01, -1.27795593e-01])
|
|
60
60
|
# Compare
|
|
61
|
-
assert np.allclose(sol, ref_sol, atol=1e-4)
|
|
61
|
+
assert np.allclose(sol, ref_sol, atol=1e-4)
|
|
62
|
+
|
|
63
|
+
def test_ADMM_matrix_form():
|
|
64
|
+
# Parameters
|
|
65
|
+
rng = np.random.default_rng(seed = 42)
|
|
66
|
+
m, n = 10, 5
|
|
67
|
+
A = rng.standard_normal((m, n))
|
|
68
|
+
b = rng.standard_normal(m)
|
|
69
|
+
|
|
70
|
+
k = 4
|
|
71
|
+
L = rng.standard_normal((k, n))
|
|
72
|
+
|
|
73
|
+
x0 = np.zeros(n)
|
|
74
|
+
sol, _ = ADMM(A, b, [(ProximalL1, np.eye(n)), (lambda z, _ : ProjectNonnegative(z), L)],
|
|
75
|
+
x0, 10, maxit = 100, adaptive = True).solve()
|
|
76
|
+
|
|
77
|
+
ref_sol = np.array([-3.99513417e-03, -1.32339656e-01, -4.52822633e-02, -7.44973888e-02, -3.35005208e-11])
|
|
78
|
+
# Compare
|
|
79
|
+
assert np.allclose(sol, ref_sol, atol=1e-4)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_ADMM_function_form():
|
|
83
|
+
# Parameters
|
|
84
|
+
rng = np.random.default_rng(seed = 42)
|
|
85
|
+
m, n = 10, 5
|
|
86
|
+
A = rng.standard_normal((m, n))
|
|
87
|
+
def A_fun(x, flag):
|
|
88
|
+
if flag == 1:
|
|
89
|
+
return A@x
|
|
90
|
+
if flag == 2:
|
|
91
|
+
return A.T@x
|
|
92
|
+
|
|
93
|
+
b = rng.standard_normal(m)
|
|
94
|
+
|
|
95
|
+
k = 4
|
|
96
|
+
L = rng.standard_normal((k, n))
|
|
97
|
+
|
|
98
|
+
x0 = np.zeros(n)
|
|
99
|
+
sol, _ = ADMM(A_fun, b, [(ProximalL1, np.eye(n)), (lambda z, _ : ProjectNonnegative(z), L)],
|
|
100
|
+
x0, 10, maxit = 100, adaptive = True).solve()
|
|
101
|
+
|
|
102
|
+
print(sol)
|
|
103
|
+
ref_sol = np.array([-3.99513417e-03, -1.32339656e-01, -4.52822633e-02, -7.44973888e-02, -3.35005208e-11])
|
|
104
|
+
# Compare
|
|
105
|
+
assert np.allclose(sol, ref_sol, atol=1e-4)
|
|
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
|
{cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_joint_distribution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_modifiedhalfnormal.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_smoothed_laplace.py
RENAMED
|
File without changes
|
{cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/distribution/_truncated_normal.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_conjugate_approx.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/experimental/mcmc/_langevin_algorithm.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.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/implicitprior/_regularizedGMRF.py
RENAMED
|
File without changes
|
{cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/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
|
{cuqipy-1.2.0.post0.dev90 → cuqipy-1.2.0.post0.dev109}/cuqi/sampler/_laplace_approximation.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.dev90 → cuqipy-1.2.0.post0.dev109}/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.dev90 → cuqipy-1.2.0.post0.dev109}/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
|
|
File without changes
|