CUQIpy 1.3.0.post0.dev70__tar.gz → 1.3.0.post0.dev104__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 (128) hide show
  1. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/CUQIpy.egg-info/PKG-INFO +3 -2
  2. cuqipy-1.3.0.post0.dev104/CUQIpy.egg-info/requires.txt +10 -0
  3. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/PKG-INFO +3 -2
  4. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/_version.py +3 -3
  5. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_truncated_normal.py +1 -1
  6. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_uniform.py +1 -1
  7. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_rto.py +23 -15
  8. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/implicitprior/_regularizedGaussian.py +8 -2
  9. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/solver/_solver.py +6 -2
  10. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/utilities/_utilities.py +1 -1
  11. cuqipy-1.3.0.post0.dev104/requirements.txt +6 -0
  12. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_bayesian_inversion.py +10 -0
  13. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_implicit_priors.py +3 -0
  14. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_model.py +1 -1
  15. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_solver.py +12 -4
  16. cuqipy-1.3.0.post0.dev70/CUQIpy.egg-info/requires.txt +0 -5
  17. cuqipy-1.3.0.post0.dev70/requirements.txt +0 -5
  18. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/CUQIpy.egg-info/SOURCES.txt +0 -0
  19. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/CUQIpy.egg-info/dependency_links.txt +0 -0
  20. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/CUQIpy.egg-info/top_level.txt +0 -0
  21. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/LICENSE +0 -0
  22. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/README.md +0 -0
  23. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/__init__.py +0 -0
  24. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/_messages.py +0 -0
  25. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/array/__init__.py +0 -0
  26. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/array/_array.py +0 -0
  27. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/config.py +0 -0
  28. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/data/__init__.py +0 -0
  29. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/data/_data.py +0 -0
  30. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/data/astronaut.npz +0 -0
  31. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/data/camera.npz +0 -0
  32. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/data/cat.npz +0 -0
  33. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/data/cookie.png +0 -0
  34. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/data/satellite.mat +0 -0
  35. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/density/__init__.py +0 -0
  36. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/density/_density.py +0 -0
  37. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/diagnostics.py +0 -0
  38. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/__init__.py +0 -0
  39. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_beta.py +0 -0
  40. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_cauchy.py +0 -0
  41. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_cmrf.py +0 -0
  42. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_custom.py +0 -0
  43. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_distribution.py +0 -0
  44. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_gamma.py +0 -0
  45. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_gaussian.py +0 -0
  46. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_gmrf.py +0 -0
  47. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_inverse_gamma.py +0 -0
  48. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_joint_distribution.py +0 -0
  49. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_laplace.py +0 -0
  50. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_lmrf.py +0 -0
  51. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_lognormal.py +0 -0
  52. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_modifiedhalfnormal.py +0 -0
  53. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_normal.py +0 -0
  54. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_posterior.py +0 -0
  55. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/distribution/_smoothed_laplace.py +0 -0
  56. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/__init__.py +0 -0
  57. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/algebra/__init__.py +0 -0
  58. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/algebra/_ast.py +0 -0
  59. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/algebra/_orderedset.py +0 -0
  60. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/algebra/_randomvariable.py +0 -0
  61. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/geometry/__init__.py +0 -0
  62. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/geometry/_productgeometry.py +0 -0
  63. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/__init__.py +0 -0
  64. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_conjugate.py +0 -0
  65. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_conjugate_approx.py +0 -0
  66. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_cwmh.py +0 -0
  67. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_direct.py +0 -0
  68. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_gibbs.py +0 -0
  69. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_hmc.py +0 -0
  70. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_langevin_algorithm.py +0 -0
  71. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_laplace_approximation.py +0 -0
  72. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_mh.py +0 -0
  73. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_pcn.py +0 -0
  74. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_sampler.py +0 -0
  75. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/experimental/mcmc/_utilities.py +0 -0
  76. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/geometry/__init__.py +0 -0
  77. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/geometry/_geometry.py +0 -0
  78. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/implicitprior/__init__.py +0 -0
  79. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/implicitprior/_regularizedGMRF.py +0 -0
  80. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/implicitprior/_regularizedUnboundedUniform.py +0 -0
  81. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/implicitprior/_restorator.py +0 -0
  82. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/likelihood/__init__.py +0 -0
  83. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/likelihood/_likelihood.py +0 -0
  84. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/model/__init__.py +0 -0
  85. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/model/_model.py +0 -0
  86. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/operator/__init__.py +0 -0
  87. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/operator/_operator.py +0 -0
  88. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/pde/__init__.py +0 -0
  89. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/pde/_pde.py +0 -0
  90. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/problem/__init__.py +0 -0
  91. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/problem/_problem.py +0 -0
  92. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/__init__.py +0 -0
  93. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_conjugate.py +0 -0
  94. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_conjugate_approx.py +0 -0
  95. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_cwmh.py +0 -0
  96. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_gibbs.py +0 -0
  97. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_hmc.py +0 -0
  98. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_langevin_algorithm.py +0 -0
  99. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_laplace_approximation.py +0 -0
  100. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_mh.py +0 -0
  101. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_pcn.py +0 -0
  102. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_rto.py +0 -0
  103. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/sampler/_sampler.py +0 -0
  104. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/samples/__init__.py +0 -0
  105. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/samples/_samples.py +0 -0
  106. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/solver/__init__.py +0 -0
  107. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/testproblem/__init__.py +0 -0
  108. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/testproblem/_testproblem.py +0 -0
  109. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/utilities/__init__.py +0 -0
  110. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/cuqi/utilities/_get_python_variable_name.py +0 -0
  111. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/pyproject.toml +0 -0
  112. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/setup.cfg +0 -0
  113. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/setup.py +0 -0
  114. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_MRFs.py +0 -0
  115. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_abstract_distribution_density.py +0 -0
  116. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_density.py +0 -0
  117. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_distribution.py +0 -0
  118. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_distributions_shape.py +0 -0
  119. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_geometry.py +0 -0
  120. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_joint_distribution.py +0 -0
  121. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_likelihood.py +0 -0
  122. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_pde.py +0 -0
  123. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_posterior.py +0 -0
  124. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_problem.py +0 -0
  125. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_sampler.py +0 -0
  126. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_samples.py +0 -0
  127. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_testproblem.py +0 -0
  128. {cuqipy-1.3.0.post0.dev70 → cuqipy-1.3.0.post0.dev104}/tests/test_utilities.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: CUQIpy
3
- Version: 1.3.0.post0.dev70
3
+ Version: 1.3.0.post0.dev104
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
@@ -202,7 +202,8 @@ Description-Content-Type: text/markdown
202
202
  License-File: LICENSE
203
203
  Requires-Dist: matplotlib
204
204
  Requires-Dist: numpy>=1.17.0
205
- Requires-Dist: scipy<1.13
205
+ Requires-Dist: scipy==1.12.0; python_version <= "3.9"
206
+ Requires-Dist: scipy; python_version > "3.9"
206
207
  Requires-Dist: arviz
207
208
  Requires-Dist: tqdm
208
209
  Dynamic: license-file
@@ -0,0 +1,10 @@
1
+ matplotlib
2
+ numpy>=1.17.0
3
+ arviz
4
+ tqdm
5
+
6
+ [:python_version <= "3.9"]
7
+ scipy==1.12.0
8
+
9
+ [:python_version > "3.9"]
10
+ scipy
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: CUQIpy
3
- Version: 1.3.0.post0.dev70
3
+ Version: 1.3.0.post0.dev104
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
@@ -202,7 +202,8 @@ Description-Content-Type: text/markdown
202
202
  License-File: LICENSE
203
203
  Requires-Dist: matplotlib
204
204
  Requires-Dist: numpy>=1.17.0
205
- Requires-Dist: scipy<1.13
205
+ Requires-Dist: scipy==1.12.0; python_version <= "3.9"
206
+ Requires-Dist: scipy; python_version > "3.9"
206
207
  Requires-Dist: arviz
207
208
  Requires-Dist: tqdm
208
209
  Dynamic: license-file
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-04-10T10:13:59+0200",
11
+ "date": "2025-04-28T12:59:25+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "c18048b15c6ec1a66e8e4bc097e0180fc3d4e47e",
15
- "version": "1.3.0.post0.dev70"
14
+ "full-revisionid": "c62aed4105837bc321e7e325f1b1a2286c659b2f",
15
+ "version": "1.3.0.post0.dev104"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -107,7 +107,7 @@ class TruncatedNormal(Distribution):
107
107
  """
108
108
  # check if x falls in the range between np.array a and b
109
109
  if np.any(x < self.low) or np.any(x > self.high):
110
- return np.NaN*np.ones_like(x)
110
+ return np.nan*np.ones_like(x)
111
111
  else:
112
112
  return self._normal.gradient(x, *args, **kwargs)
113
113
 
@@ -47,7 +47,7 @@ class Uniform(Distribution):
47
47
  Computes the gradient of logpdf at the given values of x.
48
48
  """
49
49
  if np.any(x < self.low) or np.any(x > self.high):
50
- return np.NaN*np.ones_like(x)
50
+ return np.nan*np.ones_like(x)
51
51
  else:
52
52
  return np.zeros_like(x)
53
53
 
@@ -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, ScipyLinearLSQ
6
+ from cuqi.solver import CGLS, FISTA, ADMM, ScipyLinearLSQ, ScipyMinimizer
7
7
  from cuqi.experimental.mcmc import Sampler
8
8
 
9
9
 
@@ -167,6 +167,7 @@ class RegularizedLinearRTO(LinearRTO):
167
167
  ADMM: [2] Boyd et al. "Distributed optimization and statistical learning via the alternating direction method of multipliers."Foundations and Trends® in Machine learning, 2011.
168
168
  Used when prior.proximal is a list of penalty terms.
169
169
  ScipyLinearLSQ: Wrapper for Scipy's lsq_linear for the Trust Region Reflective algorithm. Optionally used when the constraint is either "nonnegativity" or "box".
170
+ ScipyMinimizer: Wrapper for Scipy's minimize. Optionally used when the constraint is either "nonnegativity" or "box".
170
171
 
171
172
  Parameters
172
173
  ------------
@@ -177,7 +178,7 @@ class RegularizedLinearRTO(LinearRTO):
177
178
  Initial point for the sampler. *Optional*.
178
179
 
179
180
  maxit : int
180
- Maximum number of iterations of the FISTA/ADMM/ScipyLinearLSQ solver. *Optional*.
181
+ Maximum number of iterations of the FISTA/ADMM/ScipyLinearLSQ/ScipyMinimizer solver. *Optional*.
181
182
 
182
183
  inner_max_it : int
183
184
  Maximum number of iterations of the CGLS solver used within the ADMM solver. *Optional*.
@@ -191,7 +192,7 @@ class RegularizedLinearRTO(LinearRTO):
191
192
  See [2] or `cuqi.solver.ADMM`
192
193
 
193
194
  abstol : float
194
- Absolute tolerance of the FISTA/ScipyLinearLSQ solver. *Optional*.
195
+ Absolute tolerance of the FISTA/ScipyLinearLSQ/ScipyMinimizer solver. *Optional*.
195
196
 
196
197
  inner_abstol : float
197
198
  Tolerance parameter for ScipyLinearLSQ's inner solve of the unbounded least-squares problem. *Optional*.
@@ -200,7 +201,7 @@ class RegularizedLinearRTO(LinearRTO):
200
201
  If True, FISTA is used as solver, otherwise ISTA is used. *Optional*.
201
202
 
202
203
  solver : string
203
- 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*.
204
+ Options are "FISTA" (default for a single constraint or regularization), "ADMM" (default and the only option for multiple constraints or regularizations), "ScipyLinearLSQ" and "ScipyMinimizer". Note "ScipyLinearLSQ" and "ScipyMinimizer" can only be used with `RegularizedGaussian` of a single `box` or `nonnegativity` constraint. *Optional*.
204
205
 
205
206
  callback : callable, optional
206
207
  A function that will be called after each sampling step. It can be useful for monitoring the sampler during sampling.
@@ -234,11 +235,11 @@ class RegularizedLinearRTO(LinearRTO):
234
235
 
235
236
  @solver.setter
236
237
  def solver(self, value):
237
- if value == "ScipyLinearLSQ":
238
+ if value == "ScipyLinearLSQ" or value == "ScipyMinimizer":
238
239
  if (self.target.prior.preset["constraint"] == "nonnegativity" or self.target.prior.preset["constraint"] == "box"):
239
240
  self._solver = value
240
241
  else:
241
- raise ValueError("ScipyLinearLSQ only supports RegularizedGaussian with box or nonnegativity constraint.")
242
+ raise ValueError("ScipyLinearLSQ and ScipyMinimizer only support RegularizedGaussian with box or nonnegativity constraint.")
242
243
  else:
243
244
  self._solver = value
244
245
 
@@ -281,15 +282,22 @@ class RegularizedLinearRTO(LinearRTO):
281
282
  sim = ADMM(self.M, y, self.proximal,
282
283
  self.current_point, self.penalty_parameter, maxit = self.maxit, inner_max_it = self.inner_max_it, adaptive = self.adaptive)
283
284
  elif self.solver == "ScipyLinearLSQ":
284
- A_op = sp.sparse.linalg.LinearOperator((sum([llh.dim for llh in self.likelihoods])+self.target.prior.dim, self.target.prior.dim),
285
- matvec=lambda x: self.M(x, 1),
286
- rmatvec=lambda x: self.M(x, 2)
287
- )
288
- sim = ScipyLinearLSQ(A_op, y, self.target.prior._box_bounds,
289
- max_iter = self.maxit,
290
- lsmr_maxiter = self.inner_max_it,
291
- tol = self.abstol,
292
- lsmr_tol = self.inner_abstol)
285
+ A_op = sp.sparse.linalg.LinearOperator((sum([llh.distribution.dim for llh in self.likelihoods])+self.target.prior.dim, self.target.prior.dim),
286
+ matvec=lambda x: self.M(x, 1),
287
+ rmatvec=lambda x: self.M(x, 2)
288
+ )
289
+ sim = ScipyLinearLSQ(A_op, y, self.target.prior._box_bounds,
290
+ max_iter = self.maxit,
291
+ lsmr_maxiter = self.inner_max_it,
292
+ tol = self.abstol,
293
+ lsmr_tol = self.inner_abstol)
294
+ elif self.solver == "ScipyMinimizer":
295
+ # Adapt bounds format, as scipy.minimize requires a bounds format
296
+ # different than that in scipy.lsq_linear.
297
+ bounds = [(self.target.prior._box_bounds[0][i], self.target.prior._box_bounds[1][i]) for i in range(self.target.prior.dim)]
298
+ # Note that the objective function is defined as 0.5*||Mx-y||^2,
299
+ # and the corresponding gradient (gradfunc) is given by M^T(Mx-y).
300
+ sim = ScipyMinimizer(lambda x: 0.5*np.sum((self.M(x, 1)-y)**2), self.current_point, gradfunc=lambda x: self.M(self.M(x, 1) - y, 2), bounds=bounds, tol=self.abstol, options={"maxiter": self.maxit})
293
301
  else:
294
302
  raise ValueError("Choice of solver not supported.")
295
303
 
@@ -189,12 +189,18 @@ class RegularizedGaussian(Distribution):
189
189
  elif c_lower == "increasing":
190
190
  if not isinstance(self.geometry, Continuous1D):
191
191
  raise ValueError("Geometry not supported for " + c_lower)
192
- self._constraint_prox = lambda z, _: spoptimize.isotonic_regression(z, increasing=True).x
192
+ if hasattr(spoptimize, 'isotonic_regression'):
193
+ self._constraint_prox = lambda z, _: spoptimize.isotonic_regression(z, increasing=True).x
194
+ else:
195
+ raise AttributeError(f"The function 'isotonic_regression' does not exist in scipy.optimize. Installed scipy version: {spoptimize.__version__}. You need to install a scipy >= 1.12.0")
193
196
  self._preset["constraint"] = "increasing"
194
197
  elif c_lower == "decreasing":
195
198
  if not isinstance(self.geometry, Continuous1D):
196
199
  raise ValueError("Geometry not supported for " + c_lower)
197
- self._constraint_prox = lambda z, _: spoptimize.isotonic_regression(z, increasing=False).x
200
+ if hasattr(spoptimize, 'isotonic_regression'):
201
+ self._constraint_prox = lambda z, _: spoptimize.isotonic_regression(z, increasing=False).x
202
+ else:
203
+ raise AttributeError(f"The function 'isotonic_regression' does not exist in scipy.optimize. Installed scipy version: {spoptimize.__version__}. You need to install a scipy >= 1.12.0")
198
204
  self._preset["constraint"] = "decreasing"
199
205
  elif c_lower == "convex":
200
206
  if not isinstance(self.geometry, Continuous1D):
@@ -196,8 +196,11 @@ class ScipyLSQ(object):
196
196
  'trf', Trust Region Reflective algorithm: for large sparse problems with bounds.
197
197
  'dogbox', dogleg algorithm with rectangular trust regions, for small problems with bounds.
198
198
  'lm', Levenberg-Marquardt algorithm as implemented in MINPACK. Doesn't handle bounds and sparse Jacobians.
199
+ tol : The numerical tolerance for convergence checks.
200
+ maxit : The maximum number of iterations.
201
+ kwargs : Additional keyword arguments passed to scipy's least_squares. Empty by default. See documentation for scipy.optimize.least_squares
199
202
  """
200
- def __init__(self, func, x0, jacfun='2-point', method='trf', loss='linear', tol=1e-6, maxit=1e4):
203
+ def __init__(self, func, x0, jacfun='2-point', method='trf', loss='linear', tol=1e-6, maxit=1e4, **kwargs):
201
204
  self.func = func
202
205
  self.x0 = x0
203
206
  self.jacfun = jacfun
@@ -205,6 +208,7 @@ class ScipyLSQ(object):
205
208
  self.loss = loss
206
209
  self.tol = tol
207
210
  self.maxit = int(maxit)
211
+ self.kwargs = kwargs
208
212
 
209
213
  def solve(self):
210
214
  """Runs optimization algorithm and returns solution and info.
@@ -215,7 +219,7 @@ class ScipyLSQ(object):
215
219
  Solution found (array_like) and optimization information (dictionary).
216
220
  """
217
221
  solution = least_squares(self.func, self.x0, jac=self.jacfun, \
218
- method=self.method, loss=self.loss, xtol=self.tol, max_nfev=self.maxit)
222
+ method=self.method, loss=self.loss, xtol=self.tol, max_nfev=self.maxit, **self.kwargs)
219
223
  info = {"success": solution['success'],
220
224
  "message": solution['message'],
221
225
  "func": solution['fun'],
@@ -188,7 +188,7 @@ def approx_derivative(func, wrt, direction=None, epsilon=np.sqrt(np.finfo(float)
188
188
  # We compute the Jacobian matrix of func using forward differences.
189
189
  # If the function is scalar-valued, we compute the gradient instead.
190
190
  # If the direction is provided, we compute the direction-Jacobian product.
191
- wrt = np.asfarray(wrt)
191
+ wrt = np.asarray(wrt)
192
192
  f0 = func(wrt)
193
193
  Matr = np.zeros([infer_len(wrt), infer_len(f0)])
194
194
  dx = np.zeros(len(wrt))
@@ -0,0 +1,6 @@
1
+ matplotlib
2
+ numpy>=1.17.0
3
+ scipy==1.12.0; python_version <= '3.9'
4
+ scipy; python_version > '3.9'
5
+ arviz
6
+ tqdm
@@ -2,7 +2,9 @@ from typing import Dict
2
2
 
3
3
  import pytest
4
4
  import numpy as np
5
+ import scipy
5
6
  import sys
7
+ from packaging import version
6
8
 
7
9
  from cuqi.testproblem import Deconvolution1D
8
10
  from cuqi.distribution import Gaussian, GMRF, CMRF, LMRF, Gamma
@@ -25,6 +27,14 @@ def test_TP_BayesianProblem_sample(copy_reference, TP_type, phantom, prior, Ns,
25
27
  # SKIP NUTS test if not windows (for now)
26
28
  if isinstance(prior, CMRF) and not sys.platform.startswith('win'):
27
29
  pytest.skip("NUTS(CMRF) regression test is not implemented for this platform")
30
+ elif (
31
+ isinstance(prior, RegularizedGaussian)
32
+ or isinstance(prior, RegularizedGMRF)
33
+ ) and version.parse(scipy.__version__) >= version.parse('1.13.0'):
34
+ pytest.skip(
35
+ "Saved reference data is not consistent with scipy>=1.13.0 "
36
+ "(due to different default behaviour of scipy.optimize)"
37
+ )
28
38
 
29
39
  np.random.seed(19937)
30
40
 
@@ -1,6 +1,8 @@
1
1
  import cuqi
2
2
  import numpy as np
3
+ import scipy
3
4
  import pytest
5
+ from packaging import version
4
6
 
5
7
  def test_RegularizedGaussian_default_init():
6
8
  """ Test that the implicit regularized Gaussian requires at least 1 regularization argument """
@@ -186,6 +188,7 @@ def test_RegularizedGaussian_double_preset():
186
188
  assert len(x.proximal[0]) == 2
187
189
  assert len(x.proximal[1]) == 2
188
190
 
191
+ @pytest.mark.skipif(version.parse(scipy.__version__) < version.parse('1.12.0'), reason="isotonic_regression in not available in scipy < 1.12.0")
189
192
  def test_regression_increasing():
190
193
  """ Regression test for the increasing constraints in RegularizedGaussian"""
191
194
 
@@ -19,7 +19,7 @@ def test_LinearModel_getMatrix(seed):
19
19
  mat1 = model1.get_matrix() #Normal matrix
20
20
  mat2 = model2.get_matrix() #Sparse matrix (generated from functions)
21
21
 
22
- assert np.allclose(mat1,mat2.A)
22
+ assert np.allclose(mat1,mat2.toarray())
23
23
 
24
24
  def test_initialize_model_dim():
25
25
  model1 = cuqi.model.Model(lambda x:x, range_geometry=4, domain_geometry=4)
@@ -100,19 +100,27 @@ def test_ScipyLinearLSQ_with_LinearOperator():
100
100
  sol, _ = ScipyLinearLSQ(A_op, b).solve()
101
101
  assert np.allclose(sol, ref_sol, rtol=1e-10)
102
102
 
103
- def test_ScipyLinearLSQ_against_FISTA():
103
+ def test_ScipyLinearLSQ_against_ScipyMinimizer_and_against_FISTA():
104
104
  A = np.array([[73,71,52],[87,74,46],[72,2,7],[80,89,71]])
105
105
  b = np.array([49,67,68,20])
106
+
107
+ # solve with ScipyMinimizer
108
+ def fun(x):
109
+ return 0.5*np.linalg.norm(A@x-b)**2
110
+ def jac(x):
111
+ return A.T@(A@x-b)
112
+ sol_min, _ = ScipyMinimizer(fun, np.zeros(3), gradfunc=jac, tol=1e-10, bounds=[(0,np.inf),(0,np.inf),(0,np.inf)]).solve()
113
+
106
114
  # solve with ScipyLinearLSQ
107
- lb = np.zeros(3)
108
- ub = lb + np.inf
109
- sol_lsq, _ = ScipyLinearLSQ(A, b, (lb,ub)).solve()
115
+ sol_lsq, _ = ScipyLinearLSQ(A, b, ([0,0,0],[np.inf,np.inf,np.inf]), tol=1e-10).solve()
116
+
110
117
  # solve with FISTA
111
118
  rng = np.random.default_rng(seed = 1219)
112
119
  x0 = rng.standard_normal(3)
113
120
  sol_fista, _ = FISTA(A, b, lambda x, _: ProjectNonnegative(x), x0, stepsize=1e-7, maxit=100000, abstol=1e-16, adaptive=True).solve()
114
121
 
115
122
  assert np.allclose(sol_lsq, sol_fista, rtol=1e-8)
123
+ assert np.allclose(sol_min, sol_lsq, rtol=1e-8)
116
124
 
117
125
  def test_LM():
118
126
  # compare to MATLAB's original code solution
@@ -1,5 +0,0 @@
1
- matplotlib
2
- numpy>=1.17.0
3
- scipy<1.13
4
- arviz
5
- tqdm
@@ -1,5 +0,0 @@
1
- matplotlib
2
- numpy>=1.17.0
3
- scipy<1.13
4
- arviz
5
- tqdm