pygeoinf 1.0.9__py3-none-any.whl → 1.1.0__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.
- pygeoinf/__init__.py +5 -1
- pygeoinf/direct_sum.py +101 -75
- pygeoinf/forward_problem.py +33 -32
- pygeoinf/gaussian_measure.py +97 -71
- pygeoinf/hilbert_space.py +517 -241
- pygeoinf/inversion.py +16 -4
- pygeoinf/linear_bayesian.py +57 -36
- pygeoinf/linear_forms.py +169 -0
- pygeoinf/linear_optimisation.py +34 -23
- pygeoinf/linear_solvers.py +74 -247
- pygeoinf/operators.py +175 -36
- pygeoinf/random_matrix.py +36 -32
- pygeoinf/symmetric_space/circle.py +347 -202
- pygeoinf/symmetric_space/sphere.py +335 -448
- pygeoinf/symmetric_space/symmetric_space.py +330 -142
- {pygeoinf-1.0.9.dist-info → pygeoinf-1.1.0.dist-info}/METADATA +1 -2
- pygeoinf-1.1.0.dist-info/RECORD +20 -0
- pygeoinf/forms.py +0 -128
- pygeoinf/symmetric_space/line.py +0 -384
- pygeoinf-1.0.9.dist-info/RECORD +0 -21
- {pygeoinf-1.0.9.dist-info → pygeoinf-1.1.0.dist-info}/LICENSE +0 -0
- {pygeoinf-1.0.9.dist-info → pygeoinf-1.1.0.dist-info}/WHEEL +0 -0
pygeoinf/linear_solvers.py
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Provides a collection of solvers for linear systems of equations.
|
|
3
|
+
|
|
4
|
+
This module offers a unified interface for solving linear systems `A(x) = y`,
|
|
5
|
+
where `A` is a `LinearOperator`. It includes both direct methods based on
|
|
6
|
+
matrix factorization and iterative, matrix-free methods suitable for large-scale
|
|
7
|
+
problems.
|
|
8
|
+
|
|
9
|
+
The solvers are implemented as callable classes. An instance of a solver can
|
|
10
|
+
be called with an operator to produce a new operator representing its inverse.
|
|
11
|
+
|
|
12
|
+
Key Classes
|
|
13
|
+
-----------
|
|
14
|
+
- `LUSolver`, `CholeskySolver`: Direct solvers based on matrix factorization.
|
|
15
|
+
- `ScipyIterativeSolver`: A general wrapper for SciPy's iterative algorithms
|
|
16
|
+
(CG, GMRES, etc.) that operate on matrix representations.
|
|
17
|
+
- `CGSolver`: A pure, matrix-free implementation of the Conjugate Gradient
|
|
18
|
+
algorithm that operates directly on abstract Hilbert space vectors.
|
|
3
19
|
"""
|
|
4
20
|
|
|
5
21
|
from __future__ import annotations
|
|
6
22
|
from abc import ABC, abstractmethod
|
|
7
|
-
from typing import Callable
|
|
23
|
+
from typing import Callable, Optional, Dict, Any
|
|
8
24
|
|
|
9
25
|
import numpy as np
|
|
10
26
|
from scipy.sparse.linalg import LinearOperator as ScipyLinOp
|
|
@@ -17,7 +33,7 @@ from scipy.linalg import (
|
|
|
17
33
|
from scipy.sparse.linalg import gmres, bicgstab, cg, bicg
|
|
18
34
|
|
|
19
35
|
from .operators import LinearOperator
|
|
20
|
-
from .hilbert_space import
|
|
36
|
+
from .hilbert_space import Vector
|
|
21
37
|
|
|
22
38
|
|
|
23
39
|
class LinearSolver(ABC):
|
|
@@ -130,34 +146,33 @@ class IterativeLinearSolver(LinearSolver):
|
|
|
130
146
|
self,
|
|
131
147
|
operator: LinearOperator,
|
|
132
148
|
preconditioner: Optional[LinearOperator],
|
|
133
|
-
y:
|
|
134
|
-
x0: Optional[
|
|
135
|
-
) ->
|
|
149
|
+
y: Vector,
|
|
150
|
+
x0: Optional[Vector],
|
|
151
|
+
) -> Vector:
|
|
136
152
|
"""
|
|
137
153
|
Solves the linear system Ax = y for x.
|
|
138
154
|
|
|
139
155
|
Args:
|
|
140
156
|
operator (LinearOperator): The operator A of the linear system.
|
|
141
157
|
preconditioner (LinearOperator, optional): The preconditioner.
|
|
142
|
-
y (
|
|
143
|
-
x0 (
|
|
158
|
+
y (Vector): The right-hand side vector.
|
|
159
|
+
x0 (Vector, optional): The initial guess for the solution.
|
|
144
160
|
|
|
145
161
|
Returns:
|
|
146
|
-
|
|
162
|
+
Vector: The solution vector x.
|
|
147
163
|
"""
|
|
148
164
|
|
|
149
165
|
def solve_adjoint_linear_system(
|
|
150
166
|
self,
|
|
151
167
|
operator: LinearOperator,
|
|
152
168
|
preconditioner: Optional[LinearOperator],
|
|
153
|
-
x:
|
|
154
|
-
y0: Optional[
|
|
155
|
-
) ->
|
|
169
|
+
x: Vector,
|
|
170
|
+
y0: Optional[Vector],
|
|
171
|
+
) -> Vector:
|
|
156
172
|
"""
|
|
157
173
|
Solves the adjoint linear system A*y = x for y.
|
|
158
174
|
"""
|
|
159
|
-
|
|
160
|
-
return self.solve_linear_system(operator.adjoint, None, x, y0)
|
|
175
|
+
return self.solve_linear_system(operator.adjoint, preconditioner.adjoint, x, y0)
|
|
161
176
|
|
|
162
177
|
def __call__(
|
|
163
178
|
self,
|
|
@@ -189,202 +204,75 @@ class IterativeLinearSolver(LinearSolver):
|
|
|
189
204
|
)
|
|
190
205
|
|
|
191
206
|
|
|
192
|
-
class
|
|
207
|
+
class ScipyIterativeSolver(IterativeLinearSolver):
|
|
193
208
|
"""
|
|
194
|
-
|
|
195
|
-
operator's matrix representation. Assumes the operator is self-adjoint.
|
|
196
|
-
"""
|
|
197
|
-
|
|
198
|
-
def __init__(
|
|
199
|
-
self,
|
|
200
|
-
/,
|
|
201
|
-
*,
|
|
202
|
-
galerkin: bool = False,
|
|
203
|
-
rtol: float = 1.0e-5,
|
|
204
|
-
atol: float = 0.0,
|
|
205
|
-
maxiter: Optional[int] = None,
|
|
206
|
-
callback: Optional[Callable[[np.ndarray], None]] = None,
|
|
207
|
-
) -> None:
|
|
208
|
-
"""
|
|
209
|
-
Args:
|
|
210
|
-
galerkin (bool): If True, use the Galerkin matrix representation.
|
|
211
|
-
rtol (float): Relative tolerance for convergence.
|
|
212
|
-
atol (float): Absolute tolerance for convergence.
|
|
213
|
-
maxiter (int, optional): Maximum number of iterations.
|
|
214
|
-
callback (callable, optional): User-supplied function to call
|
|
215
|
-
after each iteration.
|
|
216
|
-
"""
|
|
217
|
-
self._galerkin: bool = galerkin
|
|
218
|
-
self._rtol: float = rtol
|
|
219
|
-
self._atol: float = atol
|
|
220
|
-
self._maxiter: Optional[int] = maxiter
|
|
221
|
-
self._callback: Optional[Callable[[np.ndarray], None]] = callback
|
|
209
|
+
A general iterative solver that wraps SciPy's iterative algorithms.
|
|
222
210
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
y: T_vec,
|
|
228
|
-
x0: Optional[T_vec],
|
|
229
|
-
) -> T_vec:
|
|
230
|
-
domain = operator.codomain
|
|
231
|
-
matrix = operator.matrix(galerkin=self._galerkin)
|
|
232
|
-
|
|
233
|
-
matrix_preconditioner = (
|
|
234
|
-
None
|
|
235
|
-
if preconditioner is None
|
|
236
|
-
else preconditioner.matrix(galerkin=self._galerkin)
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
cx0 = None if x0 is None else domain.to_components(x0)
|
|
240
|
-
cy = domain.to_components(y)
|
|
241
|
-
|
|
242
|
-
cxp, _ = cg(
|
|
243
|
-
matrix,
|
|
244
|
-
cy,
|
|
245
|
-
x0=cx0,
|
|
246
|
-
rtol=self._rtol,
|
|
247
|
-
atol=self._atol,
|
|
248
|
-
maxiter=self._maxiter,
|
|
249
|
-
M=matrix_preconditioner,
|
|
250
|
-
callback=self._callback,
|
|
251
|
-
)
|
|
252
|
-
if self._galerkin:
|
|
253
|
-
xp = domain.dual.from_components(cxp)
|
|
254
|
-
return domain.from_dual(xp)
|
|
255
|
-
else:
|
|
256
|
-
return domain.from_components(cxp)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
class BICGMatrixSolver(IterativeLinearSolver):
|
|
260
|
-
"""
|
|
261
|
-
Iterative solver using SciPy's Biconjugate Gradient (BiCG) algorithm on
|
|
262
|
-
the operator's matrix representation. For general square operators.
|
|
211
|
+
This class provides a unified interface to SciPy's sparse iterative
|
|
212
|
+
solvers like `cg`, `gmres`, `bicgstab`, etc. The specific algorithm is chosen
|
|
213
|
+
during instantiation, and keyword arguments are passed directly to the
|
|
214
|
+
chosen SciPy function.
|
|
263
215
|
"""
|
|
264
216
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
atol: float = 0.0,
|
|
272
|
-
maxiter: Optional[int] = None,
|
|
273
|
-
callback: Optional[Callable[[np.ndarray], None]] = None,
|
|
274
|
-
) -> None:
|
|
275
|
-
"""
|
|
276
|
-
Args:
|
|
277
|
-
galerkin (bool): If True, use the Galerkin matrix representation.
|
|
278
|
-
rtol (float): Relative tolerance for convergence.
|
|
279
|
-
atol (float): Absolute tolerance for convergence.
|
|
280
|
-
maxiter (int, optional): Maximum number of iterations.
|
|
281
|
-
callback (callable, optional): User-supplied function to call
|
|
282
|
-
after each iteration.
|
|
283
|
-
"""
|
|
284
|
-
self._galerkin: bool = galerkin
|
|
285
|
-
self._rtol: float = rtol
|
|
286
|
-
self._atol: float = atol
|
|
287
|
-
self._maxiter: Optional[int] = maxiter
|
|
288
|
-
self._callback: Optional[Callable[[np.ndarray], None]] = callback
|
|
289
|
-
|
|
290
|
-
def solve_linear_system(
|
|
291
|
-
self,
|
|
292
|
-
operator: LinearOperator,
|
|
293
|
-
preconditioner: Optional[LinearOperator],
|
|
294
|
-
y: T_vec,
|
|
295
|
-
x0: Optional[T_vec],
|
|
296
|
-
) -> T_vec:
|
|
297
|
-
domain = operator.codomain
|
|
298
|
-
codomain = operator.domain
|
|
299
|
-
matrix = operator.matrix(galerkin=self._galerkin)
|
|
300
|
-
|
|
301
|
-
matrix_preconditioner = (
|
|
302
|
-
None
|
|
303
|
-
if preconditioner is None
|
|
304
|
-
else preconditioner.matrix(galerkin=self._galerkin)
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
cx0 = None if x0 is None else domain.to_components(x0)
|
|
308
|
-
cy = domain.to_components(y)
|
|
309
|
-
|
|
310
|
-
cxp, _ = bicg(
|
|
311
|
-
matrix,
|
|
312
|
-
cy,
|
|
313
|
-
x0=cx0,
|
|
314
|
-
rtol=self._rtol,
|
|
315
|
-
atol=self._atol,
|
|
316
|
-
maxiter=self._maxiter,
|
|
317
|
-
M=matrix_preconditioner,
|
|
318
|
-
callback=self._callback,
|
|
319
|
-
)
|
|
320
|
-
if self._galerkin:
|
|
321
|
-
xp = codomain.dual.from_components(cxp)
|
|
322
|
-
return codomain.from_dual(xp)
|
|
323
|
-
else:
|
|
324
|
-
return codomain.from_components(cxp)
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
class BICGStabMatrixSolver(IterativeLinearSolver):
|
|
328
|
-
"""
|
|
329
|
-
Iterative solver using SciPy's Biconjugate Gradient Stabilized (BiCGSTAB)
|
|
330
|
-
algorithm on the operator's matrix representation. For general square operators.
|
|
331
|
-
"""
|
|
217
|
+
_SOLVER_MAP = {
|
|
218
|
+
"cg": cg,
|
|
219
|
+
"bicg": bicg,
|
|
220
|
+
"bicgstab": bicgstab,
|
|
221
|
+
"gmres": gmres,
|
|
222
|
+
}
|
|
332
223
|
|
|
333
224
|
def __init__(
|
|
334
225
|
self,
|
|
226
|
+
method: str,
|
|
335
227
|
/,
|
|
336
228
|
*,
|
|
337
229
|
galerkin: bool = False,
|
|
338
|
-
|
|
339
|
-
atol: float = 0.0,
|
|
340
|
-
maxiter: Optional[int] = None,
|
|
341
|
-
callback: Optional[Callable[[np.ndarray], None]] = None,
|
|
230
|
+
**kwargs,
|
|
342
231
|
) -> None:
|
|
343
232
|
"""
|
|
344
233
|
Args:
|
|
234
|
+
method (str): The name of the SciPy solver to use (e.g., 'cg', 'gmres').
|
|
345
235
|
galerkin (bool): If True, use the Galerkin matrix representation.
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
maxiter (int, optional): Maximum number of iterations.
|
|
349
|
-
callback (callable, optional): User-supplied function to call
|
|
350
|
-
after each iteration.
|
|
236
|
+
**kwargs: Keyword arguments to be passed directly to the SciPy solver
|
|
237
|
+
(e.g., rtol, atol, maxiter, restart).
|
|
351
238
|
"""
|
|
239
|
+
if method not in self._SOLVER_MAP:
|
|
240
|
+
raise ValueError(
|
|
241
|
+
f"Unknown solver method '{method}'. Available methods: {list(self._SOLVER_MAP.keys())}"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
self._solver_func = self._SOLVER_MAP[method]
|
|
352
245
|
self._galerkin: bool = galerkin
|
|
353
|
-
self.
|
|
354
|
-
self._atol: float = atol
|
|
355
|
-
self._maxiter: Optional[int] = maxiter
|
|
356
|
-
self._callback: Optional[Callable[[np.ndarray], None]] = callback
|
|
246
|
+
self._solver_kwargs: Dict[str, Any] = kwargs
|
|
357
247
|
|
|
358
248
|
def solve_linear_system(
|
|
359
249
|
self,
|
|
360
250
|
operator: LinearOperator,
|
|
361
251
|
preconditioner: Optional[LinearOperator],
|
|
362
|
-
y:
|
|
363
|
-
x0: Optional[
|
|
364
|
-
) ->
|
|
252
|
+
y: Vector,
|
|
253
|
+
x0: Optional[Vector],
|
|
254
|
+
) -> Vector:
|
|
365
255
|
domain = operator.codomain
|
|
366
256
|
codomain = operator.domain
|
|
367
|
-
matrix = operator.matrix(galerkin=self._galerkin)
|
|
368
257
|
|
|
258
|
+
matrix = operator.matrix(galerkin=self._galerkin)
|
|
369
259
|
matrix_preconditioner = (
|
|
370
260
|
None
|
|
371
261
|
if preconditioner is None
|
|
372
262
|
else preconditioner.matrix(galerkin=self._galerkin)
|
|
373
263
|
)
|
|
374
264
|
|
|
375
|
-
cx0 = None if x0 is None else domain.to_components(x0)
|
|
376
265
|
cy = domain.to_components(y)
|
|
266
|
+
cx0 = None if x0 is None else domain.to_components(x0)
|
|
377
267
|
|
|
378
|
-
cxp, _ =
|
|
268
|
+
cxp, _ = self._solver_func(
|
|
379
269
|
matrix,
|
|
380
270
|
cy,
|
|
381
271
|
x0=cx0,
|
|
382
|
-
rtol=self._rtol,
|
|
383
|
-
atol=self._atol,
|
|
384
|
-
maxiter=self._maxiter,
|
|
385
272
|
M=matrix_preconditioner,
|
|
386
|
-
|
|
273
|
+
**self._solver_kwargs,
|
|
387
274
|
)
|
|
275
|
+
|
|
388
276
|
if self._galerkin:
|
|
389
277
|
xp = codomain.dual.from_components(cxp)
|
|
390
278
|
return codomain.from_dual(xp)
|
|
@@ -392,81 +280,20 @@ class BICGStabMatrixSolver(IterativeLinearSolver):
|
|
|
392
280
|
return codomain.from_components(cxp)
|
|
393
281
|
|
|
394
282
|
|
|
395
|
-
|
|
396
|
-
""
|
|
397
|
-
Iterative solver using SciPy's Generalized Minimal Residual (GMRES)
|
|
398
|
-
algorithm on the operator's matrix representation. For general square operators.
|
|
399
|
-
"""
|
|
283
|
+
def CGMatrixSolver(galerkin: bool = False, **kwargs) -> ScipyIterativeSolver:
|
|
284
|
+
return ScipyIterativeSolver("cg", galerkin=galerkin, **kwargs)
|
|
400
285
|
|
|
401
|
-
def __init__(
|
|
402
|
-
self,
|
|
403
|
-
/,
|
|
404
|
-
*,
|
|
405
|
-
galerkin: bool = False,
|
|
406
|
-
rtol: float = 1.0e-5,
|
|
407
|
-
atol: float = 0.0,
|
|
408
|
-
restart: Optional[int] = None,
|
|
409
|
-
maxiter: Optional[int] = None,
|
|
410
|
-
callback: Optional[Callable] = None,
|
|
411
|
-
callback_type: Optional[str] = None,
|
|
412
|
-
) -> None:
|
|
413
|
-
"""
|
|
414
|
-
Args:
|
|
415
|
-
galerkin (bool): If True, use the Galerkin matrix representation.
|
|
416
|
-
rtol (float): Relative tolerance for convergence.
|
|
417
|
-
atol (float): Absolute tolerance for convergence.
|
|
418
|
-
restart (int, optional): Number of iterations between restarts.
|
|
419
|
-
maxiter (int, optional): Maximum number of iterations.
|
|
420
|
-
callback (callable, optional): User-supplied function to call
|
|
421
|
-
during iterations.
|
|
422
|
-
callback_type (str, optional): Type of callback ("x", "pr_norm").
|
|
423
|
-
"""
|
|
424
|
-
self._galerkin: bool = galerkin
|
|
425
|
-
self._rtol: float = rtol
|
|
426
|
-
self._atol: float = atol
|
|
427
|
-
self._restart: Optional[int] = restart
|
|
428
|
-
self._maxiter: Optional[int] = maxiter
|
|
429
|
-
self._callback: Optional[Callable] = callback
|
|
430
|
-
self._callback_type: Optional[str] = callback_type
|
|
431
286
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
operator: LinearOperator,
|
|
435
|
-
preconditioner: Optional[LinearOperator],
|
|
436
|
-
y: T_vec,
|
|
437
|
-
x0: Optional[T_vec],
|
|
438
|
-
) -> T_vec:
|
|
439
|
-
domain = operator.codomain
|
|
440
|
-
codomain = operator.domain
|
|
441
|
-
matrix = operator.matrix(galerkin=self._galerkin)
|
|
287
|
+
def BICGMatrixSolver(galerkin: bool = False, **kwargs) -> ScipyIterativeSolver:
|
|
288
|
+
return ScipyIterativeSolver("bicg", galerkin=galerkin, **kwargs)
|
|
442
289
|
|
|
443
|
-
matrix_preconditioner = (
|
|
444
|
-
None
|
|
445
|
-
if preconditioner is None
|
|
446
|
-
else preconditioner.matrix(galerkin=self._galerkin)
|
|
447
|
-
)
|
|
448
290
|
|
|
449
|
-
|
|
450
|
-
|
|
291
|
+
def BICGStabMatrixSolver(galerkin: bool = False, **kwargs) -> ScipyIterativeSolver:
|
|
292
|
+
return ScipyIterativeSolver("bicgstab", galerkin=galerkin, **kwargs)
|
|
451
293
|
|
|
452
|
-
cxp, _ = gmres(
|
|
453
|
-
matrix,
|
|
454
|
-
cy,
|
|
455
|
-
x0=cx0,
|
|
456
|
-
rtol=self._rtol,
|
|
457
|
-
atol=self._atol,
|
|
458
|
-
restart=self._restart,
|
|
459
|
-
maxiter=self._maxiter,
|
|
460
|
-
M=matrix_preconditioner,
|
|
461
|
-
callback=self._callback,
|
|
462
|
-
callback_type=self._callback_type,
|
|
463
|
-
)
|
|
464
294
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
return codomain.from_dual(xp)
|
|
468
|
-
else:
|
|
469
|
-
return codomain.from_components(cxp)
|
|
295
|
+
def GMRESMatrixSolver(galerkin: bool = False, **kwargs) -> ScipyIterativeSolver:
|
|
296
|
+
return ScipyIterativeSolver("gmres", galerkin=galerkin, **kwargs)
|
|
470
297
|
|
|
471
298
|
|
|
472
299
|
class CGSolver(IterativeLinearSolver):
|
|
@@ -485,7 +312,7 @@ class CGSolver(IterativeLinearSolver):
|
|
|
485
312
|
rtol: float = 1.0e-5,
|
|
486
313
|
atol: float = 0.0,
|
|
487
314
|
maxiter: Optional[int] = None,
|
|
488
|
-
callback: Optional[Callable[[
|
|
315
|
+
callback: Optional[Callable[[Vector], None]] = None,
|
|
489
316
|
) -> None:
|
|
490
317
|
"""
|
|
491
318
|
Args:
|
|
@@ -507,15 +334,15 @@ class CGSolver(IterativeLinearSolver):
|
|
|
507
334
|
raise ValueError("maxiter must be None or positive")
|
|
508
335
|
self._maxiter: Optional[int] = maxiter
|
|
509
336
|
|
|
510
|
-
self._callback: Optional[Callable[[
|
|
337
|
+
self._callback: Optional[Callable[[Vector], None]] = callback
|
|
511
338
|
|
|
512
339
|
def solve_linear_system(
|
|
513
340
|
self,
|
|
514
341
|
operator: LinearOperator,
|
|
515
342
|
preconditioner: Optional[LinearOperator],
|
|
516
|
-
y:
|
|
517
|
-
x0: Optional[
|
|
518
|
-
) ->
|
|
343
|
+
y: Vector,
|
|
344
|
+
x0: Optional[Vector],
|
|
345
|
+
) -> Vector:
|
|
519
346
|
domain = operator.domain
|
|
520
347
|
x = domain.zero if x0 is None else domain.copy(x0)
|
|
521
348
|
|