CUQIpy 1.2.0.post0.dev409__tar.gz → 1.2.0.post0.dev444__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.

Files changed (126) hide show
  1. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/CUQIpy.egg-info/PKG-INFO +1 -1
  2. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/PKG-INFO +1 -1
  3. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/_version.py +3 -3
  4. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_rto.py +47 -13
  5. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/implicitprior/_regularizedGaussian.py +5 -3
  6. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/solver/__init__.py +2 -1
  7. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/solver/_solver.py +39 -1
  8. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_solver.py +43 -5
  9. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/CUQIpy.egg-info/SOURCES.txt +0 -0
  10. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/CUQIpy.egg-info/dependency_links.txt +0 -0
  11. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/CUQIpy.egg-info/requires.txt +0 -0
  12. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/CUQIpy.egg-info/top_level.txt +0 -0
  13. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/LICENSE +0 -0
  14. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/README.md +0 -0
  15. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/__init__.py +0 -0
  16. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/_messages.py +0 -0
  17. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/array/__init__.py +0 -0
  18. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/array/_array.py +0 -0
  19. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/config.py +0 -0
  20. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/data/__init__.py +0 -0
  21. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/data/_data.py +0 -0
  22. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/data/astronaut.npz +0 -0
  23. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/data/camera.npz +0 -0
  24. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/data/cat.npz +0 -0
  25. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/data/cookie.png +0 -0
  26. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/data/satellite.mat +0 -0
  27. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/density/__init__.py +0 -0
  28. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/density/_density.py +0 -0
  29. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/diagnostics.py +0 -0
  30. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/__init__.py +0 -0
  31. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_beta.py +0 -0
  32. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_cauchy.py +0 -0
  33. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_cmrf.py +0 -0
  34. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_custom.py +0 -0
  35. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_distribution.py +0 -0
  36. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_gamma.py +0 -0
  37. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_gaussian.py +0 -0
  38. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_gmrf.py +0 -0
  39. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_inverse_gamma.py +0 -0
  40. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_joint_distribution.py +0 -0
  41. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_laplace.py +0 -0
  42. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_lmrf.py +0 -0
  43. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_lognormal.py +0 -0
  44. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_modifiedhalfnormal.py +0 -0
  45. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_normal.py +0 -0
  46. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_posterior.py +0 -0
  47. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_smoothed_laplace.py +0 -0
  48. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_truncated_normal.py +0 -0
  49. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/distribution/_uniform.py +0 -0
  50. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/__init__.py +0 -0
  51. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/algebra/__init__.py +0 -0
  52. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/algebra/_ast.py +0 -0
  53. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/algebra/_orderedset.py +0 -0
  54. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/algebra/_randomvariable.py +0 -0
  55. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/geometry/__init__.py +0 -0
  56. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/geometry/_productgeometry.py +0 -0
  57. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/__init__.py +0 -0
  58. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_conjugate.py +0 -0
  59. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_conjugate_approx.py +0 -0
  60. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_cwmh.py +0 -0
  61. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_direct.py +0 -0
  62. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_gibbs.py +0 -0
  63. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_hmc.py +0 -0
  64. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_langevin_algorithm.py +0 -0
  65. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_laplace_approximation.py +0 -0
  66. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_mh.py +0 -0
  67. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_pcn.py +0 -0
  68. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_sampler.py +0 -0
  69. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/experimental/mcmc/_utilities.py +0 -0
  70. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/geometry/__init__.py +0 -0
  71. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/geometry/_geometry.py +0 -0
  72. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/implicitprior/__init__.py +0 -0
  73. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/implicitprior/_regularizedGMRF.py +0 -0
  74. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/implicitprior/_regularizedUnboundedUniform.py +0 -0
  75. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/implicitprior/_restorator.py +0 -0
  76. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/likelihood/__init__.py +0 -0
  77. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/likelihood/_likelihood.py +0 -0
  78. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/model/__init__.py +0 -0
  79. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/model/_model.py +0 -0
  80. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/operator/__init__.py +0 -0
  81. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/operator/_operator.py +0 -0
  82. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/pde/__init__.py +0 -0
  83. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/pde/_pde.py +0 -0
  84. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/problem/__init__.py +0 -0
  85. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/problem/_problem.py +0 -0
  86. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/__init__.py +0 -0
  87. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_conjugate.py +0 -0
  88. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_conjugate_approx.py +0 -0
  89. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_cwmh.py +0 -0
  90. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_gibbs.py +0 -0
  91. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_hmc.py +0 -0
  92. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_langevin_algorithm.py +0 -0
  93. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_laplace_approximation.py +0 -0
  94. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_mh.py +0 -0
  95. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_pcn.py +0 -0
  96. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_rto.py +0 -0
  97. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/sampler/_sampler.py +0 -0
  98. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/samples/__init__.py +0 -0
  99. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/samples/_samples.py +0 -0
  100. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/testproblem/__init__.py +0 -0
  101. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/testproblem/_testproblem.py +0 -0
  102. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/utilities/__init__.py +0 -0
  103. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/utilities/_get_python_variable_name.py +0 -0
  104. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/cuqi/utilities/_utilities.py +0 -0
  105. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/pyproject.toml +0 -0
  106. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/requirements.txt +0 -0
  107. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/setup.cfg +0 -0
  108. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/setup.py +0 -0
  109. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_MRFs.py +0 -0
  110. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_abstract_distribution_density.py +0 -0
  111. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_bayesian_inversion.py +0 -0
  112. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_density.py +0 -0
  113. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_distribution.py +0 -0
  114. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_distributions_shape.py +0 -0
  115. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_geometry.py +0 -0
  116. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_implicit_priors.py +0 -0
  117. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_joint_distribution.py +0 -0
  118. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_likelihood.py +0 -0
  119. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_model.py +0 -0
  120. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_pde.py +0 -0
  121. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_posterior.py +0 -0
  122. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_problem.py +0 -0
  123. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_sampler.py +0 -0
  124. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_samples.py +0 -0
  125. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_testproblem.py +0 -0
  126. {cuqipy-1.2.0.post0.dev409 → cuqipy-1.2.0.post0.dev444}/tests/test_utilities.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: CUQIpy
3
- Version: 1.2.0.post0.dev409
3
+ Version: 1.2.0.post0.dev444
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.2
2
2
  Name: CUQIpy
3
- Version: 1.2.0.post0.dev409
3
+ Version: 1.2.0.post0.dev444
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": "2025-01-14T23:39:18+0100",
11
+ "date": "2025-01-17T09:21:17+0100",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "4d0949ea5c777b95d3c216f22c17e267e5cf15ff",
15
- "version": "1.2.0.post0.dev409"
14
+ "full-revisionid": "0b7a72688059f892d521f7e332065b2ccdc456bd",
15
+ "version": "1.2.0.post0.dev444"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -3,7 +3,7 @@ from scipy.linalg.interpolative import estimate_spectral_norm
3
3
  from scipy.sparse.linalg import LinearOperator as scipyLinearOperator
4
4
  import numpy as np
5
5
  import cuqi
6
- from cuqi.solver import CGLS, FISTA, ADMM
6
+ from cuqi.solver import CGLS, FISTA, ADMM, ScipyLinearLSQ
7
7
  from cuqi.experimental.mcmc import Sampler
8
8
 
9
9
 
@@ -168,6 +168,7 @@ class RegularizedLinearRTO(LinearRTO):
168
168
  Used when prior.proximal is callable.
169
169
  ADMM: [2] Boyd et al. "Distributed optimization and statistical learning via the alternating direction method of multipliers."Foundations and Trends® in Machine learning, 2011.
170
170
  Used when prior.proximal is a list of penalty terms.
171
+ ScipyLinearLSQ: Wrapper for Scipy's lsq_linear for the Trust Region Reflective algorithm. Optionally used when the constraint is either "nonnegativity" or "box".
171
172
 
172
173
  Parameters
173
174
  ------------
@@ -178,7 +179,7 @@ class RegularizedLinearRTO(LinearRTO):
178
179
  Initial point for the sampler. *Optional*.
179
180
 
180
181
  maxit : int
181
- Maximum number of iterations of the inner FISTA/ADMM solver. *Optional*.
182
+ Maximum number of iterations of the FISTA/ADMM/ScipyLinearLSQ solver. *Optional*.
182
183
 
183
184
  inner_max_it : int
184
185
  Maximum number of iterations of the CGLS solver used within the ADMM solver. *Optional*.
@@ -188,14 +189,20 @@ class RegularizedLinearRTO(LinearRTO):
188
189
  If stepsize is a float, then this stepsize is used.
189
190
 
190
191
  penalty_parameter : int
191
- Penalty parameter of the inner ADMM solver. *Optional*.
192
+ Penalty parameter of the ADMM solver. *Optional*.
192
193
  See [2] or `cuqi.solver.ADMM`
193
194
 
194
195
  abstol : float
195
- Absolute tolerance of the inner FISTA solver. *Optional*.
196
+ Absolute tolerance of the FISTA/ScipyLinearLSQ solver. *Optional*.
197
+
198
+ inner_abstol : float
199
+ Tolerance parameter for ScipyLinearLSQ's inner solve of the unbounded least-squares problem. *Optional*.
196
200
 
197
201
  adaptive : bool
198
- If True, FISTA is used as inner solver, otherwise ISTA is used. *Optional*.
202
+ If True, FISTA is used as solver, otherwise ISTA is used. *Optional*.
203
+
204
+ solver : string
205
+ If set to "ScipyLinearLSQ", solver is set to cuqi.solver.ScipyLinearLSQ, otherwise FISTA/ISTA or ADMM is used. Note "ScipyLinearLSQ" can only be used with `RegularizedGaussian` of `box` or `nonnegativity` constraint. *Optional*.
199
206
 
200
207
  callback : callable, *Optional*
201
208
  If set this function will be called after every sample.
@@ -204,23 +211,41 @@ class RegularizedLinearRTO(LinearRTO):
204
211
  An example is shown in demos/demo31_callback.py.
205
212
 
206
213
  """
207
- def __init__(self, target=None, initial_point=None, maxit=100, inner_max_it=10, stepsize="automatic", penalty_parameter=10, abstol=1e-10, adaptive=True, **kwargs):
214
+ def __init__(self, target=None, initial_point=None, maxit=100, inner_max_it=10, stepsize="automatic", penalty_parameter=10, abstol=1e-10, adaptive=True, solver=None, inner_abstol=None, **kwargs):
208
215
 
209
216
  super().__init__(target=target, initial_point=initial_point, **kwargs)
210
217
 
211
218
  # Other parameters
212
219
  self.stepsize = stepsize
213
- self.abstol = abstol
220
+ self.abstol = abstol
221
+ self.inner_abstol = inner_abstol
214
222
  self.adaptive = adaptive
215
223
  self.maxit = maxit
216
224
  self.inner_max_it = inner_max_it
217
225
  self.penalty_parameter = penalty_parameter
226
+ self.solver = solver
218
227
 
219
228
  def _initialize(self):
220
229
  super()._initialize()
221
- if self._inner_solver == "FISTA":
230
+ if self.solver is None:
231
+ self.solver = "FISTA" if callable(self.proximal) else "ADMM"
232
+ if self.solver == "FISTA":
222
233
  self._stepsize = self._choose_stepsize()
223
234
 
235
+ @property
236
+ def solver(self):
237
+ return self._solver
238
+
239
+ @solver.setter
240
+ def solver(self, value):
241
+ if value == "ScipyLinearLSQ":
242
+ if (self.target.prior._preset == "nonnegativity" or self.target.prior._preset == "box"):
243
+ self._solver = value
244
+ else:
245
+ raise ValueError("ScipyLinearLSQ only supports RegularizedGaussian with box or nonnegativity constraint.")
246
+ else:
247
+ self._solver = value
248
+
224
249
  @property
225
250
  def proximal(self):
226
251
  return self.target.prior.proximal
@@ -229,7 +254,6 @@ class RegularizedLinearRTO(LinearRTO):
229
254
  super().validate_target()
230
255
  if not isinstance(self.target.prior, (cuqi.implicitprior.RegularizedGaussian, cuqi.implicitprior.RegularizedGMRF)):
231
256
  raise TypeError("Prior needs to be RegularizedGaussian or RegularizedGMRF")
232
- self._inner_solver = "FISTA" if callable(self.proximal) else "ADMM"
233
257
 
234
258
  def _choose_stepsize(self):
235
259
  if isinstance(self.stepsize, str):
@@ -254,15 +278,25 @@ class RegularizedLinearRTO(LinearRTO):
254
278
  def step(self):
255
279
  y = self.b_tild + np.random.randn(len(self.b_tild))
256
280
 
257
- if self._inner_solver == "FISTA":
281
+ if self.solver == "FISTA":
258
282
  sim = FISTA(self.M, y, self.proximal,
259
283
  self.current_point, maxit = self.maxit, stepsize = self._stepsize, abstol = self.abstol, adaptive = self.adaptive)
260
- elif self._inner_solver == "ADMM":
284
+ elif self.solver == "ADMM":
261
285
  sim = ADMM(self.M, y, self.proximal,
262
- self.current_point, self.penalty_parameter, maxit = self.maxit, inner_max_it = self.inner_max_it, adaptive = self.adaptive)
286
+ self.current_point, self.penalty_parameter, maxit = self.maxit, inner_max_it = self.inner_max_it, adaptive = self.adaptive)
287
+ elif self.solver == "ScipyLinearLSQ":
288
+ A_op = sp.sparse.linalg.LinearOperator((sum([llh.dim for llh in self.likelihoods])+self.target.prior.dim, self.target.prior.dim),
289
+ matvec=lambda x: self.M(x, 1),
290
+ rmatvec=lambda x: self.M(x, 2)
291
+ )
292
+ sim = ScipyLinearLSQ(A_op, y, self.target.prior._box_bounds,
293
+ max_iter = self.maxit,
294
+ lsmr_maxiter = self.inner_max_it,
295
+ tol = self.abstol,
296
+ lsmr_tol = self.inner_abstol)
263
297
  else:
264
298
  raise ValueError("Choice of solver not supported.")
265
299
 
266
300
  self.current_point, _ = sim.solve()
267
301
  acc = 1
268
- return acc
302
+ return acc
@@ -113,10 +113,12 @@ class RegularizedGaussian(Distribution):
113
113
  elif (isinstance(constraint, str) and constraint.lower() == "nonnegativity"):
114
114
  self._proximal = lambda z, gamma: ProjectNonnegative(z)
115
115
  self._preset = "nonnegativity"
116
+ self._box_bounds = (np.ones(self.dim)*0, np.ones(self.dim)*np.inf)
116
117
  elif (isinstance(constraint, str) and constraint.lower() == "box"):
117
- lower = optional_regularization_parameters["lower_bound"]
118
- upper = optional_regularization_parameters["upper_bound"]
119
- self._proximal = lambda z, _: ProjectBox(z, lower, upper)
118
+ self._box_lower = optional_regularization_parameters["lower_bound"]
119
+ self._box_upper = optional_regularization_parameters["upper_bound"]
120
+ self._box_bounds = (np.ones(self.dim)*self._box_lower, np.ones(self.dim)*self._box_upper)
121
+ self._proximal = lambda z, _: ProjectBox(z, self._box_lower, self._box_upper)
120
122
  self._preset = "box" # Not supported in Gibbs
121
123
  elif (isinstance(regularization, str) and regularization.lower() in ["l1"]):
122
124
  self._strength = optional_regularization_parameters["strength"]
@@ -2,7 +2,8 @@ from ._solver import (
2
2
  ScipyLBFGSB,
3
3
  ScipyMinimizer,
4
4
  ScipyMaximizer,
5
- ScipyLeastSquares,
5
+ ScipyLSQ,
6
+ ScipyLinearLSQ,
6
7
  CGLS,
7
8
  LM,
8
9
  PDHG,
@@ -164,7 +164,7 @@ class ScipyMaximizer(ScipyMinimizer):
164
164
 
165
165
 
166
166
 
167
- class ScipyLeastSquares(object):
167
+ class ScipyLSQ(object):
168
168
  """Wrapper for :meth:`scipy.optimize.least_squares`.
169
169
 
170
170
  Solve nonlinear least-squares problems with bounds:
@@ -227,6 +227,44 @@ class ScipyLeastSquares(object):
227
227
  sol = solution['x']
228
228
  return sol, info
229
229
 
230
+ class ScipyLinearLSQ(object):
231
+ """Wrapper for :meth:`scipy.optimize.lsq_linear`.
232
+
233
+ Solve linear least-squares problems with bounds:
234
+
235
+ .. math::
236
+
237
+ \min \|A x - b\|_2^2
238
+
239
+ subject to :math:`lb <= x <= ub`.
240
+
241
+ Parameters
242
+ ----------
243
+ A : ndarray, LinearOperator
244
+ Design matrix (system matrix).
245
+ b : ndarray
246
+ The right-hand side of the linear system.
247
+ bounds : 2-tuple of array_like or scipy.optimize Bounds
248
+ Bounds for variables.
249
+ kwargs : Other keyword arguments passed to Scipy's `lsq_linear`. See documentation of `scipy.optimize.lsq_linear` for details.
250
+ """
251
+ def __init__(self, A, b, bounds=(-np.inf, np.inf), **kwargs):
252
+ self.A = A
253
+ self.b = b
254
+ self.bounds = bounds
255
+ self.kwargs = kwargs
256
+
257
+ def solve(self):
258
+ """Runs optimization algorithm and returns solution and optimization information.
259
+
260
+ Returns
261
+ ----------
262
+ solution : Tuple
263
+ Solution found (array_like) and optimization information (dictionary).
264
+ """
265
+ res = opt.lsq_linear(self.A, self.b, bounds=self.bounds, **self.kwargs)
266
+ x = res.pop('x')
267
+ return x, res
230
268
 
231
269
 
232
270
  class CGLS(object):
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  import scipy as sp
3
3
 
4
- from cuqi.solver import ScipyLBFGSB, ScipyMinimizer, ScipyLeastSquares, CGLS, LM, FISTA, ADMM, ProximalL1, ProjectNonnegative
4
+ from cuqi.solver import ScipyLBFGSB, ScipyMinimizer, ScipyLSQ, ScipyLinearLSQ, CGLS, LM, FISTA, ADMM, ProximalL1, ProjectNonnegative, ProjectBox
5
5
  from scipy.optimize import lsq_linear
6
6
 
7
7
 
@@ -54,16 +54,16 @@ def test_ScipyLBFGSB_with_gradient():
54
54
  sol_ref = np.array([1.0, 1.0, 1.0, 1.0, 1.0])
55
55
  assert np.allclose(sol, sol_ref)
56
56
 
57
- def test_ScipyLeastSquares_without_Jac():
57
+ def test_ScipyLSQ_without_Jac():
58
58
  def fun_rosenbrock(x):
59
59
  return np.array([10 * (x[1] - x[0]**2), (1 - x[0])])
60
60
  x0 = np.array([2, 2])
61
- solver = ScipyLeastSquares(fun_rosenbrock, x0)
61
+ solver = ScipyLSQ(fun_rosenbrock, x0)
62
62
  sol, _ = solver.solve()
63
63
  sol_ref = np.array([1, 1])
64
64
  assert np.allclose(sol, sol_ref)
65
65
 
66
- def test_ScipyLeastSquares_with_Jac():
66
+ def test_ScipyLSQ_with_Jac():
67
67
  def fun_rosenbrock(x):
68
68
  return np.array([10 * (x[1] - x[0]**2), (1 - x[0])])
69
69
  def jac_rosenbrock(x):
@@ -71,11 +71,49 @@ def test_ScipyLeastSquares_with_Jac():
71
71
  [-20 * x[0], 10],
72
72
  [-1, 0]])
73
73
  x0 = np.array([2, 2])
74
- solver = ScipyLeastSquares(fun_rosenbrock, x0, jacfun=jac_rosenbrock)
74
+ solver = ScipyLSQ(fun_rosenbrock, x0, jacfun=jac_rosenbrock)
75
75
  sol, _ = solver.solve()
76
76
  sol_ref = np.array([1, 1])
77
77
  assert np.allclose(sol, sol_ref)
78
78
 
79
+ def test_ScipyLinearLSQ_with_matrix():
80
+ rng = np.random.default_rng(seed = 1219)
81
+ m, n = 10, 5
82
+ A = rng.standard_normal((m, n))
83
+ b = rng.standard_normal(m)
84
+ res = lsq_linear(A, b, tol=1e-8)
85
+ ref_sol = res.x
86
+ sol, _ = ScipyLinearLSQ(A, b).solve()
87
+ assert np.allclose(sol, ref_sol, rtol=1e-10)
88
+
89
+ def test_ScipyLinearLSQ_with_LinearOperator():
90
+ rng = np.random.default_rng(seed = 1219)
91
+ m, n = 10, 5
92
+ A = rng.standard_normal((m, n))
93
+ b = rng.standard_normal(m)
94
+ A_op = sp.sparse.linalg.LinearOperator((m, n),
95
+ matvec=lambda x: A @ x,
96
+ rmatvec=lambda x: A.T @ x
97
+ )
98
+ res = lsq_linear(A, b, tol=1e-8)
99
+ ref_sol = res.x
100
+ sol, _ = ScipyLinearLSQ(A_op, b).solve()
101
+ assert np.allclose(sol, ref_sol, rtol=1e-10)
102
+
103
+ def test_ScipyLinearLSQ_against_FISTA():
104
+ A = np.array([[73,71,52],[87,74,46],[72,2,7],[80,89,71]])
105
+ b = np.array([49,67,68,20])
106
+ # solve with ScipyLinearLSQ
107
+ lb = np.zeros(3)
108
+ ub = lb + np.inf
109
+ sol_lsq, _ = ScipyLinearLSQ(A, b, (lb,ub)).solve()
110
+ # solve with FISTA
111
+ rng = np.random.default_rng(seed = 1219)
112
+ x0 = rng.standard_normal(3)
113
+ sol_fista, _ = FISTA(A, b, lambda x, _: ProjectNonnegative(x), x0, stepsize=1e-7, maxit=100000, abstol=1e-16, adaptive=True).solve()
114
+
115
+ assert np.allclose(sol_lsq, sol_fista, rtol=1e-8)
116
+
79
117
  def test_LM():
80
118
  # compare to MATLAB's original code solution
81
119
  t = np.arange(1, 10, 2)