CUQIpy 1.3.0.post0.dev70__py3-none-any.whl → 1.3.0.post0.dev104__py3-none-any.whl

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.

cuqi/_version.py CHANGED
@@ -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):
cuqi/solver/_solver.py CHANGED
@@ -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))
@@ -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
@@ -1,6 +1,6 @@
1
1
  cuqi/__init__.py,sha256=LsGilhl-hBLEn6Glt8S_l0OJzAA1sKit_rui8h-D-p0,488
2
2
  cuqi/_messages.py,sha256=fzEBrZT2kbmfecBBPm7spVu7yHdxGARQB4QzXhJbCJ0,415
3
- cuqi/_version.py,sha256=x3zpGp2Yt3uRSPN-QiV1WPjz_E6y4Vd8e_5YzWKqC-4,509
3
+ cuqi/_version.py,sha256=guMao82iJ7XuaWXVsBLkNDaBLZLETh0C0JtSWy0RCeA,510
4
4
  cuqi/config.py,sha256=wcYvz19wkeKW2EKCGIKJiTpWt5kdaxyt4imyRkvtTRA,526
5
5
  cuqi/diagnostics.py,sha256=5OrbJeqpynqRXOe5MtOKKhe7EAVdOEpHIqHnlMW9G_c,3029
6
6
  cuqi/array/__init__.py,sha256=-EeiaiWGNsE3twRS4dD814BIlfxEsNkTCZUc5gjOXb0,30
@@ -32,8 +32,8 @@ cuqi/distribution/_modifiedhalfnormal.py,sha256=ErULXUFRjbMyCYywaOzfuxtoy-XQmC0M
32
32
  cuqi/distribution/_normal.py,sha256=vhIiAseW09IKh1uy0KUq7RP1IuY7hH5aNM1W_R8Gd_Q,2912
33
33
  cuqi/distribution/_posterior.py,sha256=zAfL0GECxekZ2lBt1W6_LN0U_xskMwK4VNce5xAF7ig,5018
34
34
  cuqi/distribution/_smoothed_laplace.py,sha256=p-1Y23mYA9omwiHGkEuv3T2mwcPAAoNlCr7T8osNkjE,2925
35
- cuqi/distribution/_truncated_normal.py,sha256=sPaveXTij_yRZg-609zfwzGjHJOzZVenvYaYK0krUec,4240
36
- cuqi/distribution/_uniform.py,sha256=eONkPlVX3EIZ9x1vHa3Ul60NJsbuWVCabTuAJVH2GGk,3255
35
+ cuqi/distribution/_truncated_normal.py,sha256=_ez3MmO6qpBeP6BKCUlW3IgxuF7k--A7jPGPUhtYK0g,4240
36
+ cuqi/distribution/_uniform.py,sha256=fVgj_4SBav8JMc1pNAO1l_CZ9ZwdoMIpN9iQ3i9_Z0Q,3255
37
37
  cuqi/experimental/__init__.py,sha256=bIQ9OroeitHbwgNe3wI_JvzkILK0N25Tt7wpquPoU3w,129
38
38
  cuqi/experimental/algebra/__init__.py,sha256=btRAWG58ZfdtK0afXKOg60AX7d76KMBjlZa4AWBCCgU,81
39
39
  cuqi/experimental/algebra/_ast.py,sha256=PdPz19cJMjvnMx4KEzhn4gvxIZX_UViE33Mbttj_5Xw,9873
@@ -52,14 +52,14 @@ cuqi/experimental/mcmc/_langevin_algorithm.py,sha256=1UunuocpzG1h6GiYefEHFOMykEM
52
52
  cuqi/experimental/mcmc/_laplace_approximation.py,sha256=I5ZLtU0lA34YflRbqxKi5UgJBhhHzxqUyVW5JE5-l2w,5916
53
53
  cuqi/experimental/mcmc/_mh.py,sha256=MXo0ahXP4KGFkaY4HtvcBE-TMQzsMlTmLKzSvpz7drU,2941
54
54
  cuqi/experimental/mcmc/_pcn.py,sha256=wqJBZLuRFSwxihaI53tumAg6AWVuceLMOmXssTetd1A,3374
55
- cuqi/experimental/mcmc/_rto.py,sha256=2RJm1NMIvmCbTBn0M8S8vNLaRjaS1_7S9gVOdG9I8Bo,14159
55
+ cuqi/experimental/mcmc/_rto.py,sha256=BY55Njw3-dcmjd-V1vQ58CisEDllQ8zaEj92pWB6LCM,15158
56
56
  cuqi/experimental/mcmc/_sampler.py,sha256=VK-VsPRaYET43C5quhu2f1OstEX5DKYKVyjKABTRHZE,20288
57
57
  cuqi/experimental/mcmc/_utilities.py,sha256=kUzHbhIS3HYZRbneNBK41IogUYX5dS_bJxqEGm7TQBI,525
58
58
  cuqi/geometry/__init__.py,sha256=Tz1WGzZBY-QGH3c0GiyKm9XHN8MGGcnU6TUHLZkzB3o,842
59
59
  cuqi/geometry/_geometry.py,sha256=W-oQTZPelVS7fN9qZj6bNBuh-yY0eqOHJ39UwB-WmQY,47562
60
60
  cuqi/implicitprior/__init__.py,sha256=6z3lvw-tWDyjZSpB3pYzvijSMK9Zlf1IYqOVTtMD2h4,309
61
61
  cuqi/implicitprior/_regularizedGMRF.py,sha256=BUeT4rwJzary9K56fkxCNGCeKZd-2VSgOT8XNHxFPRE,6345
62
- cuqi/implicitprior/_regularizedGaussian.py,sha256=gFGtIJH6Zh7vwIrQ2yON4x5VtzJngTa4mCyGOYEm9ac,21252
62
+ cuqi/implicitprior/_regularizedGaussian.py,sha256=9BSKHGEW0OT9OIt_42strDzxBM8mB6A-blcf0kEguHw,21836
63
63
  cuqi/implicitprior/_regularizedUnboundedUniform.py,sha256=uHGYYnTjVxdPbY-5JwocFOH0sHRfGrrLiHWahzH9R8A,3533
64
64
  cuqi/implicitprior/_restorator.py,sha256=Z350XUJEt7N59Qw-SIUaBljQNDJk4Zb0i_KRFrt2DCg,10087
65
65
  cuqi/likelihood/__init__.py,sha256=QXif382iwZ5bT3ZUqmMs_n70JVbbjxbqMrlQYbMn4Zo,1776
@@ -87,14 +87,14 @@ cuqi/sampler/_sampler.py,sha256=TkZ_WAS-5Q43oICa-Elc2gftsRTBd7PEDUMDZ9tTGmU,5712
87
87
  cuqi/samples/__init__.py,sha256=vCs6lVk-pi8RBqa6cIN5wyn6u-K9oEf1Na4k1ZMrYv8,44
88
88
  cuqi/samples/_samples.py,sha256=hUc8OnCF9CTCuDTrGHwwzv3wp8mG_6vsJAFvuQ-x0uA,35832
89
89
  cuqi/solver/__init__.py,sha256=KYgAi_8VoAwljTB3S2I87YnJkRtedskLee7hQp_-zp8,220
90
- cuqi/solver/_solver.py,sha256=_Q47Atv8Ze6eMJzA22s0OzdW4lcDigRhbotnCzmrQWE,30662
90
+ cuqi/solver/_solver.py,sha256=X3EWD-26o9UOBsWRuy0ktYsJiUXwpCGm0lvTdQM6dRI,30964
91
91
  cuqi/testproblem/__init__.py,sha256=DWTOcyuNHMbhEuuWlY5CkYkNDSAqhvsKmJXBLivyblU,202
92
92
  cuqi/testproblem/_testproblem.py,sha256=x769LwwRdJdzIiZkcQUGb_5-vynNTNALXWKato7sS0Q,52540
93
93
  cuqi/utilities/__init__.py,sha256=d5QXRzmI6EchS9T4b7eTezSisPWuWklO8ey4YBx9kI0,569
94
94
  cuqi/utilities/_get_python_variable_name.py,sha256=wxpCaj9f3ZtBNqlGmmuGiITgBaTsY-r94lUIlK6UAU4,2043
95
- cuqi/utilities/_utilities.py,sha256=en4woCU0LJvdrnmjns5OMLMmRguWWTQxhBMuFGxPK4U,18415
96
- cuqipy-1.3.0.post0.dev70.dist-info/licenses/LICENSE,sha256=kJWRPrtRoQoZGXyyvu50Uc91X6_0XRaVfT0YZssicys,10799
97
- cuqipy-1.3.0.post0.dev70.dist-info/METADATA,sha256=p9-ZWhhvEJ2jhjPRIldlRrO6bF2hGYKN9AeEuBYs0WM,18550
98
- cuqipy-1.3.0.post0.dev70.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
99
- cuqipy-1.3.0.post0.dev70.dist-info/top_level.txt,sha256=AgmgMc6TKfPPqbjV0kvAoCBN334i_Lwwojc7HE3ZwD0,5
100
- cuqipy-1.3.0.post0.dev70.dist-info/RECORD,,
95
+ cuqi/utilities/_utilities.py,sha256=as8cFswoxROS0Z7WUKzLIE-ZtEKCXes5M3Gdmmb47No,18414
96
+ cuqipy-1.3.0.post0.dev104.dist-info/licenses/LICENSE,sha256=kJWRPrtRoQoZGXyyvu50Uc91X6_0XRaVfT0YZssicys,10799
97
+ cuqipy-1.3.0.post0.dev104.dist-info/METADATA,sha256=ocRbxXFZqKPsVVnPjFNgacaxLE8zeXDD71iHGk6q6j8,18624
98
+ cuqipy-1.3.0.post0.dev104.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
99
+ cuqipy-1.3.0.post0.dev104.dist-info/top_level.txt,sha256=AgmgMc6TKfPPqbjV0kvAoCBN334i_Lwwojc7HE3ZwD0,5
100
+ cuqipy-1.3.0.post0.dev104.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5